@p4code/cli 0.5.16 → 0.5.19

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/dist/bin.mjs CHANGED
@@ -153,7 +153,7 @@ const closeServer = (server) => {
153
153
  * NetService - Service tag for startup networking helpers.
154
154
  */
155
155
  var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
156
- const make$120 = () => {
156
+ const make$122 = () => {
157
157
  /**
158
158
  * Returns true when a TCP server can bind to {host, port}.
159
159
  * `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
@@ -262,10 +262,10 @@ const make$120 = () => {
262
262
  })
263
263
  };
264
264
  };
265
- const layer$122 = Layer.sync(NetService, make$120);
265
+ const layer$122 = Layer.sync(NetService, make$122);
266
266
  //#endregion
267
267
  //#region package.json
268
- var version$1 = "0.5.16";
268
+ var version$1 = "0.5.19";
269
269
  //#endregion
270
270
  //#region src/config.ts
271
271
  /**
@@ -286,8 +286,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
286
286
  /** @deprecated Import and use `layerTest` from this module. */
287
287
  static layerTest = (cwd, baseDirOrPrefix) => layerTest$4(cwd, baseDirOrPrefix);
288
288
  };
289
- const make$119 = (config) => ServerConfig$1.of(config);
290
- const layer$121 = (config) => Layer.succeed(ServerConfig$1, make$119(config));
289
+ const make$121 = (config) => ServerConfig$1.of(config);
290
+ const layer$121 = (config) => Layer.succeed(ServerConfig$1, make$121(config));
291
291
  const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
292
292
  const { join } = yield* Path$1.Path;
293
293
  const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
@@ -3521,6 +3521,21 @@ const OrchestrationThreadActivity = Schema$1.Struct({
3521
3521
  sequence: Schema$1.optional(NonNegativeInt),
3522
3522
  createdAt: IsoDateTime
3523
3523
  });
3524
+ /**
3525
+ * The activity a turn gets when Jev (TypeSafe System One) answered for it.
3526
+ *
3527
+ * One per consultation, never merged: a turn that took a skill hint and a
3528
+ * workspace choice from Jev carries two, and each names what it decided.
3529
+ * Silence, a timeout or a declined answer append nothing, so the row's
3530
+ * presence is the whole claim.
3531
+ */
3532
+ const JEV_CONSULTED_ACTIVITY_KIND = "jev.consulted";
3533
+ const JevConsultedFeature = Schema$1.Literals(["capability-hints", "auto-workspace"]);
3534
+ Schema$1.Struct({
3535
+ feature: JevConsultedFeature,
3536
+ /** What Jev decided, in one short clause: "Suggested: design, publish". */
3537
+ summary: TrimmedNonEmptyString
3538
+ });
3524
3539
  const OrchestrationLatestTurnState = Schema$1.Literals([
3525
3540
  "running",
3526
3541
  "interrupted",
@@ -10400,6 +10415,11 @@ const ClientSettingsSchema = Schema$1.Struct({
10400
10415
  })).pipe(Schema$1.withDecodingDefault(Effect.succeed(13)))
10401
10416
  });
10402
10417
  const DEFAULT_CLIENT_SETTINGS = Schema$1.decodeSync(ClientSettingsSchema)({});
10418
+ const DEFAULT_JEV_PREFETCH_TIMEOUT_MS = 2e3;
10419
+ const JevPrefetchTimeoutMs = Schema$1.Int.check(Schema$1.isBetween({
10420
+ minimum: 100,
10421
+ maximum: 3e4
10422
+ }));
10403
10423
  const ThreadEnvMode = Schema$1.Literals([
10404
10424
  "auto",
10405
10425
  "local",
@@ -10766,6 +10786,32 @@ const ServerSettings = Schema$1.Struct({
10766
10786
  /** Supervisor wake pre-filter; disabled by default. */
10767
10787
  fusionReviewTriage: FusionReviewTriageSettings.pipe(Schema$1.withDecodingDefault(Effect.succeed(Schema$1.decodeUnknownSync(FusionReviewTriageSettings)({})))),
10768
10788
  /**
10789
+ * Whether a turn asks Jev which of this environment's capabilities to name
10790
+ * to the model before its first request.
10791
+ *
10792
+ * One environment-wide switch rather than a per-thread control: which skills
10793
+ * and commands exist is a fact about the machine, not about a conversation,
10794
+ * and nobody wants to answer the same question on every new thread. The
10795
+ * ranking is advisory - it adds a few names to the turn and changes nothing
10796
+ * else - so the cost of leaving it on is a short request, which is why it is
10797
+ * on by default.
10798
+ *
10799
+ * Off removes the request entirely. So does a missing Jev key, a timeout or
10800
+ * an unreadable answer: every one of those is the same turn the agent would
10801
+ * have had before this setting existed.
10802
+ */
10803
+ enableJevPrefetch: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
10804
+ /** Maximum per-turn wait for capability hints; failures continue without hints. */
10805
+ jevPrefetchTimeoutMs: JevPrefetchTimeoutMs.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_JEV_PREFETCH_TIMEOUT_MS))),
10806
+ /**
10807
+ * Whether Auto checkout mode asks Jev to choose between the current
10808
+ * checkout and a fresh worktree before falling back to the text generation
10809
+ * model. Same shape as `enableJevPrefetch`: environment-wide, on by
10810
+ * default, and every failure path is the decision the turn would have
10811
+ * made without it.
10812
+ */
10813
+ enableJevAutoWorkspace: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
10814
+ /**
10769
10815
  * Whether the board offers the scoping agent: the Scope action on a task and
10770
10816
  * the panel that drafts a new one by interview.
10771
10817
  *
@@ -10995,6 +11041,9 @@ const ServerSettingsPatch = Schema$1.Struct({
10995
11041
  fusionSummarizerModel: Schema$1.optionalKey(Schema$1.NullOr(ModelSelection)),
10996
11042
  fusionPromptOverrides: Schema$1.optionalKey(FusionPromptOverrides),
10997
11043
  fusionReviewTriage: Schema$1.optionalKey(FusionReviewTriageSettings),
11044
+ enableJevPrefetch: Schema$1.optionalKey(Schema$1.Boolean),
11045
+ jevPrefetchTimeoutMs: Schema$1.optionalKey(JevPrefetchTimeoutMs),
11046
+ enableJevAutoWorkspace: Schema$1.optionalKey(Schema$1.Boolean),
10998
11047
  enableScopingAgent: Schema$1.optionalKey(Schema$1.Boolean),
10999
11048
  enablePlanPhase: Schema$1.optionalKey(Schema$1.Boolean),
11000
11049
  writeGeneratedSkillsToRepo: Schema$1.optionalKey(Schema$1.Boolean),
@@ -12575,7 +12624,7 @@ const ThreadSpawnInput = Schema$1.Struct({
12575
12624
  interactionMode: Schema$1.optional(ProviderInteractionMode),
12576
12625
  compressMode: Schema$1.optional(CompressMode),
12577
12626
  unpromptedSubagents: Schema$1.optional(Schema$1.Boolean),
12578
- fusion: Schema$1.optional(Schema$1.Boolean.annotate({ description: "Create two new threads as a separate Fusion pair, then start the builder with prompt. Leaves the caller and its existing pair unchanged. Cannot be combined with fusionWatcher." })),
12627
+ fusion: Schema$1.optional(Schema$1.Boolean.annotate({ description: "Create two new threads as a separate Fusion pair, then start the supervisor with prompt. The builder stays empty until advised. Leaves the caller and its existing pair unchanged. Cannot be combined with fusionWatcher." })),
12579
12628
  fusionWatcher: Schema$1.optional(Schema$1.Boolean.annotate({ description: "Set true only when creating the watcher to pair with the current thread. Defaults to the saved Fusion supervisor model. No special user command syntax is required." }))
12580
12629
  });
12581
12630
  const ThreadSpawnResult = Schema$1.Struct({
@@ -12587,16 +12636,16 @@ const ThreadSpawnResult = Schema$1.Struct({
12587
12636
  watcherThreadId: Schema$1.optional(ThreadId)
12588
12637
  });
12589
12638
  const ThreadPairCreateInput = Schema$1.Struct({
12590
- watcherThreadId: ThreadId.annotate({ description: "The supervisor thread to pair with this session's own builder thread. It must have been created by this session through thread_spawn." }),
12639
+ watcherThreadId: Schema$1.optional(ThreadId.annotate({ description: "Legacy pairing: use an existing spawned supervisor and keep this chat as builder. Omit to keep this chat and its history as supervisor and create a new empty builder, idle until advised." })),
12591
12640
  gateTimeoutMs: Schema$1.optional(NonNegativeInt.annotate({ description: "How long a gate waits on the supervisor before failing open, in milliseconds. Defaults to 120000." }))
12592
12641
  });
12593
12642
  const ThreadPairCreateResult = Schema$1.Struct({
12594
12643
  pairId: ThreadPairId,
12595
12644
  implementerThreadId: ThreadId,
12596
12645
  watcherThreadId: ThreadId,
12597
- continuationMessageId: MessageId
12646
+ continuationMessageId: Schema$1.optional(MessageId)
12598
12647
  });
12599
- const ThreadPairDetachInput = Schema$1.Struct({ pairId: ThreadPairId.annotate({ description: "The active Fusion pair to detach. This session must be its implementer and must have created its watcher with thread_spawn." }) });
12648
+ const ThreadPairDetachInput = Schema$1.Struct({ pairId: ThreadPairId.annotate({ description: "The active Fusion pair to detach. This session must belong to the pair and must have created its other thread." }) });
12600
12649
  const ThreadPairDetachResult = Schema$1.Struct({
12601
12650
  pairId: ThreadPairId,
12602
12651
  implementerThreadId: ThreadId,
@@ -16072,7 +16121,7 @@ function deriveAuthClientMetadata(input) {
16072
16121
  //#endregion
16073
16122
  //#region src/auth/EnvironmentAuthPolicy.ts
16074
16123
  var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
16075
- const make$118 = Effect.gen(function* () {
16124
+ const make$120 = Effect.gen(function* () {
16076
16125
  const config = yield* ServerConfig$1;
16077
16126
  const isRemoteReachable = isRemoteReachableHost(config.host);
16078
16127
  const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
@@ -16090,7 +16139,7 @@ const make$118 = Effect.gen(function* () {
16090
16139
  };
16091
16140
  return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
16092
16141
  });
16093
- const layer$120 = Layer.effect(EnvironmentAuthPolicy, make$118);
16142
+ const layer$120 = Layer.effect(EnvironmentAuthPolicy, make$120);
16094
16143
  //#endregion
16095
16144
  //#region src/persistence/Errors.ts
16096
16145
  function summarizeSchemaIssue(issue) {
@@ -16271,7 +16320,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
16271
16320
  cause
16272
16321
  });
16273
16322
  }
16274
- const make$117 = Effect.gen(function* () {
16323
+ const make$119 = Effect.gen(function* () {
16275
16324
  const sql = yield* SqlClient.SqlClient;
16276
16325
  const createSessionRow = SqlSchema.void({
16277
16326
  Request: CreateAuthSessionInput,
@@ -16405,7 +16454,7 @@ const make$117 = Effect.gen(function* () {
16405
16454
  setLastConnectedAt
16406
16455
  };
16407
16456
  });
16408
- const layer$119 = Layer.effect(AuthSessionRepository, make$117);
16457
+ const layer$119 = Layer.effect(AuthSessionRepository, make$119);
16409
16458
  //#endregion
16410
16459
  //#region src/auth/ServerSecretStore.ts
16411
16460
  const secretStoreErrorContext = {
@@ -16547,11 +16596,11 @@ const makeForDirectory = Effect.fn("ServerSecretStore.makeForDirectory")(functio
16547
16596
  remove
16548
16597
  });
16549
16598
  });
16550
- const make$116 = Effect.gen(function* () {
16599
+ const make$118 = Effect.gen(function* () {
16551
16600
  const config = yield* ServerConfig$1;
16552
16601
  return yield* makeForDirectory(config.secretsDir);
16553
16602
  });
16554
- const layer$118 = Layer.effect(ServerSecretStore, make$116);
16603
+ const layer$118 = Layer.effect(ServerSecretStore, make$118);
16555
16604
  //#endregion
16556
16605
  //#region src/auth/SessionStore.ts
16557
16606
  var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
@@ -16789,7 +16838,7 @@ function toAuthClientSession(input) {
16789
16838
  current: false
16790
16839
  };
16791
16840
  }
16792
- const make$115 = Effect.gen(function* () {
16841
+ const make$117 = Effect.gen(function* () {
16793
16842
  const crypto = yield* Crypto.Crypto;
16794
16843
  const serverConfig = yield* ServerConfig$1;
16795
16844
  const secretStore = yield* ServerSecretStore;
@@ -17104,7 +17153,7 @@ const make$115 = Effect.gen(function* () {
17104
17153
  markDisconnected
17105
17154
  });
17106
17155
  });
17107
- const layer$117 = Layer.effect(SessionStore, make$115).pipe(Layer.provideMerge(layer$119));
17156
+ const layer$117 = Layer.effect(SessionStore, make$117).pipe(Layer.provideMerge(layer$119));
17108
17157
  //#endregion
17109
17158
  //#region src/persistence/AuthPairingLinks.ts
17110
17159
  const AuthPairingLinkRecord = Schema$1.Struct({
@@ -17165,7 +17214,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
17165
17214
  cause
17166
17215
  });
17167
17216
  }
17168
- const make$114 = Effect.gen(function* () {
17217
+ const make$116 = Effect.gen(function* () {
17169
17218
  const sql = yield* SqlClient.SqlClient;
17170
17219
  const createPairingLinkRow = SqlSchema.void({
17171
17220
  Request: CreateAuthPairingLinkInput,
@@ -17300,7 +17349,7 @@ const make$114 = Effect.gen(function* () {
17300
17349
  getByCredential
17301
17350
  };
17302
17351
  });
17303
- const layer$116 = Layer.effect(AuthPairingLinkRepository, make$114);
17352
+ const layer$116 = Layer.effect(AuthPairingLinkRepository, make$116);
17304
17353
  //#endregion
17305
17354
  //#region src/auth/PairingGrantStore.ts
17306
17355
  var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
@@ -17395,7 +17444,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
17395
17444
  const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
17396
17445
  const PAIRING_TOKEN_LENGTH = 12;
17397
17446
  const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
17398
- const make$113 = Effect.gen(function* () {
17447
+ const make$115 = Effect.gen(function* () {
17399
17448
  const crypto = yield* Crypto.Crypto;
17400
17449
  const config = yield* ServerConfig$1;
17401
17450
  const pairingLinks = yield* AuthPairingLinkRepository;
@@ -17593,7 +17642,7 @@ const make$113 = Effect.gen(function* () {
17593
17642
  consume
17594
17643
  });
17595
17644
  });
17596
- const layer$115 = Layer.effect(PairingGrantStore, make$113).pipe(Layer.provideMerge(layer$116));
17645
+ const layer$115 = Layer.effect(PairingGrantStore, make$115).pipe(Layer.provideMerge(layer$116));
17597
17646
  //#endregion
17598
17647
  //#region src/persistence/DatabaseSnapshot.ts
17599
17648
  /**
@@ -20978,7 +21027,7 @@ function parseBearerToken(request) {
20978
21027
  const token = header.slice(7).trim();
20979
21028
  return token.length > 0 ? token : null;
20980
21029
  }
20981
- const make$112 = Effect.gen(function* () {
21030
+ const make$114 = Effect.gen(function* () {
20982
21031
  const policy = yield* EnvironmentAuthPolicy;
20983
21032
  const bootstrapCredentials = yield* PairingGrantStore;
20984
21033
  const sessions = yield* SessionStore;
@@ -21173,7 +21222,7 @@ const make$112 = Effect.gen(function* () {
21173
21222
  issueStartupPairingUrl
21174
21223
  });
21175
21224
  });
21176
- const layer$114 = Layer.effect(EnvironmentAuth, make$112).pipe(Layer.provideMerge(layer$115), Layer.provideMerge(layer$117), Layer.provideMerge(layer$120));
21225
+ const layer$114 = Layer.effect(EnvironmentAuth, make$114).pipe(Layer.provideMerge(layer$115), Layer.provideMerge(layer$117), Layer.provideMerge(layer$120));
21177
21226
  const storageLayer = Layer.mergeAll(layer$118, layerConfig);
21178
21227
  const runtimeLayer = layer$114.pipe(Layer.provideMerge(storageLayer));
21179
21228
  //#endregion
@@ -23169,7 +23218,7 @@ const DEFAULT_LIMITS = {
23169
23218
  windowMillis: FAILURE_WINDOW_MS$1,
23170
23219
  blockMillis: BLOCK_DURATION_MS
23171
23220
  };
23172
- const make$111 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
23221
+ const make$113 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
23173
23222
  const state = yield* Ref.make(initialThrottleState);
23174
23223
  return HubAuthThrottle.of({
23175
23224
  shouldRefuse: Effect.gen(function* () {
@@ -23183,7 +23232,7 @@ const make$111 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_L
23183
23232
  })
23184
23233
  });
23185
23234
  });
23186
- const layer$113 = Layer.effect(HubAuthThrottle, make$111());
23235
+ const layer$113 = Layer.effect(HubAuthThrottle, make$113());
23187
23236
  //#endregion
23188
23237
  //#region src/hub/HubAuth.ts
23189
23238
  /**
@@ -25165,12 +25214,12 @@ const persistHubAgentActivity = Effect.fn("HubNotificationActivity.persist")(fun
25165
25214
  var HubSecretStoreUnavailable = class extends Data.TaggedError("HubSecretStoreUnavailable") {};
25166
25215
  const HUB_SECRETS_SUFFIX = ".secrets";
25167
25216
  /** Dedicated sibling directory per Hub database; no environment configuration is consulted. */
25168
- const make$110 = Effect.gen(function* () {
25217
+ const make$112 = Effect.gen(function* () {
25169
25218
  const config = yield* HubConfig;
25170
25219
  if (!(yield* Path$1.Path).isAbsolute(config.dbPath)) return yield* Effect.fail(new HubSecretStoreUnavailable({ message: "Hub secret storage requires an absolute database path." }));
25171
25220
  return yield* makeForDirectory(`${config.dbPath}${HUB_SECRETS_SUFFIX}`);
25172
25221
  });
25173
- const layer$107 = Layer.effect(ServerSecretStore, make$110);
25222
+ const layer$107 = Layer.effect(ServerSecretStore, make$112);
25174
25223
  //#endregion
25175
25224
  //#region src/hub/HubNotificationRoutes.ts
25176
25225
  const MAX_NOTIFICATION_BODY_BYTES = 16 * 1024;
@@ -29268,7 +29317,7 @@ function stripDefaultServerSettings(current, defaults) {
29268
29317
  }
29269
29318
  return Object.is(current, defaults) ? void 0 : current;
29270
29319
  }
29271
- const make$109 = Effect.gen(function* () {
29320
+ const make$111 = Effect.gen(function* () {
29272
29321
  const { settingsPath } = yield* ServerConfig$1;
29273
29322
  const fs = yield* FileSystem.FileSystem;
29274
29323
  const pathService = yield* Path$1.Path;
@@ -29491,7 +29540,7 @@ const make$109 = Effect.gen(function* () {
29491
29540
  }
29492
29541
  };
29493
29542
  });
29494
- const layer$93 = Layer.effect(ServerSettingsService, make$109);
29543
+ const layer$93 = Layer.effect(ServerSettingsService, make$111);
29495
29544
  //#endregion
29496
29545
  //#region src/pathExpansion.ts
29497
29546
  /**
@@ -29731,13 +29780,17 @@ function parseMarkdownFrontmatter(contents) {
29731
29780
  const name = typeof record.name === "string" ? record.name.trim() : "";
29732
29781
  const description = typeof record.description === "string" ? record.description.trim() : "";
29733
29782
  const disableModelInvocation = readFrontmatterBoolean(record["disable-model-invocation"]);
29783
+ const tools = Array.isArray(record.tools) ? record.tools.flatMap((tool) => typeof tool === "string" && tool.trim() ? [tool.trim()] : []) : void 0;
29784
+ const model = typeof record.model === "string" ? record.model.trim() : "";
29734
29785
  const requiredInteractionMode = record["required-interaction-mode"] === "default" || record["required-interaction-mode"] === "plan" ? record["required-interaction-mode"] : void 0;
29735
29786
  return {
29736
29787
  kind: "parsed",
29737
29788
  ...name ? { name } : {},
29738
29789
  ...description ? { description } : {},
29739
29790
  ...disableModelInvocation === void 0 ? {} : { disableModelInvocation },
29740
- ...requiredInteractionMode === void 0 ? {} : { requiredInteractionMode }
29791
+ ...requiredInteractionMode === void 0 ? {} : { requiredInteractionMode },
29792
+ ...tools && tools.length > 0 ? { tools } : {},
29793
+ ...model ? { model } : {}
29741
29794
  };
29742
29795
  }
29743
29796
  //#endregion
@@ -30041,6 +30094,42 @@ const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* (config
30041
30094
  scope: "project"
30042
30095
  }] : []], disabledSkills);
30043
30096
  });
30097
+ /**
30098
+ * Load named subagent definitions from the user config dir.
30099
+ *
30100
+ * The CLI finds these itself whenever the `user` setting source is loaded, so
30101
+ * nothing calls this on an ordinary session. It exists for the sessions that
30102
+ * drop that source and still owe the user a specific agent - a Fusion half
30103
+ * under the lean context profile, which must still run the delivery reviewer.
30104
+ * Reading only the names asked for keeps that session from paying for the rest
30105
+ * of the user's agents, which is what dropping the source bought.
30106
+ *
30107
+ * Best-effort like the skill discovery beside it: a missing or malformed
30108
+ * definition is skipped rather than failing the session, because a session
30109
+ * that cannot start is worse than one missing an agent.
30110
+ */
30111
+ const discoverClaudeUserAgents = Effect.fn("discoverClaudeUserAgents")(function* (config, names, environment, cwd) {
30112
+ if (names.length === 0) return {};
30113
+ const fileSystem = yield* FileSystem.FileSystem;
30114
+ const path = yield* Path$1.Path;
30115
+ const agentsDir = yield* resolveClaudeUserAgentsDir(config, environment, cwd);
30116
+ const definitions = {};
30117
+ for (const name of names) {
30118
+ const contents = yield* fileSystem.readFileString(path.join(agentsDir, `${name}.md`)).pipe(Effect.orElseSucceed(() => void 0));
30119
+ if (contents === void 0) continue;
30120
+ const frontmatter = parseMarkdownFrontmatter(contents);
30121
+ if (frontmatter.kind !== "parsed") continue;
30122
+ const prompt = stripMarkdownFrontmatter(contents);
30123
+ if (frontmatter.description === void 0 || prompt.length === 0) continue;
30124
+ definitions[name] = {
30125
+ description: frontmatter.description,
30126
+ prompt,
30127
+ ...frontmatter.tools ? { tools: frontmatter.tools } : {},
30128
+ ...frontmatter.model ? { model: frontmatter.model } : {}
30129
+ };
30130
+ }
30131
+ return definitions;
30132
+ });
30044
30133
  //#endregion
30045
30134
  //#region src/mcp/ClaudeMcpFiles.ts
30046
30135
  /**
@@ -30153,7 +30242,7 @@ function claudeEntryFromRegistration(registration) {
30153
30242
  };
30154
30243
  }
30155
30244
  var ClaudeMcpFiles = class extends Context.Service()("@p4code/cli/mcp/ClaudeMcpFiles") {};
30156
- const make$108 = Effect.gen(function* () {
30245
+ const make$110 = Effect.gen(function* () {
30157
30246
  const fileSystem = yield* FileSystem.FileSystem;
30158
30247
  const path = yield* Path$1.Path;
30159
30248
  const services = yield* Effect.context();
@@ -30220,7 +30309,7 @@ const make$108 = Effect.gen(function* () {
30220
30309
  removeProject: (projectDir, name) => removeAt(Effect.succeed(projectFile(projectDir)))(name)
30221
30310
  };
30222
30311
  });
30223
- const layer$92 = Layer.effect(ClaudeMcpFiles, make$108);
30312
+ const layer$92 = Layer.effect(ClaudeMcpFiles, make$110);
30224
30313
  Layer.succeed(ClaudeMcpFiles, {
30225
30314
  readUser: Effect.succeed([]),
30226
30315
  readUserAt: () => Effect.succeed([]),
@@ -30358,7 +30447,7 @@ const decodeClientRegistration = Schema$1.decodeUnknownExit(ClientRegistrationRe
30358
30447
  const decodeTokenResponse = Schema$1.decodeUnknownExit(TokenResponse);
30359
30448
  var McpOAuth = class extends Context.Service()("@p4code/cli/mcp/McpOAuth") {};
30360
30449
  const registryError = (detail) => new McpRegistryError({ detail });
30361
- const make$107 = Effect.gen(function* () {
30450
+ const make$109 = Effect.gen(function* () {
30362
30451
  const config = yield* ServerConfig$1;
30363
30452
  const secrets = yield* ServerSecretStore;
30364
30453
  const http = yield* HttpClient.HttpClient;
@@ -30678,7 +30767,7 @@ const make$107 = Effect.gen(function* () {
30678
30767
  accessTokenFor
30679
30768
  };
30680
30769
  });
30681
- const layer$91 = Layer.effect(McpOAuth, make$107);
30770
+ const layer$91 = Layer.effect(McpOAuth, make$109);
30682
30771
  Layer.succeed(McpOAuth, {
30683
30772
  statusFor: () => Effect.succeed(Option.none()),
30684
30773
  begin: () => Effect.fail(new McpRegistryError({ detail: "OAuth sign-in is not available." })),
@@ -30696,7 +30785,7 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
30696
30785
  const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
30697
30786
  var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
30698
30787
  const slotsOf = (registration) => registration.secrets ?? [];
30699
- const make$106 = Effect.gen(function* () {
30788
+ const make$108 = Effect.gen(function* () {
30700
30789
  const config = yield* ServerConfig$1;
30701
30790
  const secrets = yield* ServerSecretStore;
30702
30791
  const oauth = yield* McpOAuth;
@@ -30870,7 +30959,7 @@ const make$106 = Effect.gen(function* () {
30870
30959
  resolveForSessionAtClaudeUserConfigPath
30871
30960
  };
30872
30961
  });
30873
- const layer$90 = Layer.effect(McpRegistry, make$106);
30962
+ const layer$90 = Layer.effect(McpRegistry, make$108);
30874
30963
  //#endregion
30875
30964
  //#region src/sync/skillDirectory.ts
30876
30965
  /**
@@ -31249,7 +31338,7 @@ const formatHubLink = (input) => encodeStoredHubLink({
31249
31338
  shareMode: input.shareMode
31250
31339
  });
31251
31340
  const fromEnvironment = (environment) => validateHubLink(environment.P4CODE_HUB_URL ?? "", environment.P4CODE_HUB_TOKEN ?? "");
31252
- const make$105 = Effect.fn("HubLink.make")(function* (environment) {
31341
+ const make$107 = Effect.fn("HubLink.make")(function* (environment) {
31253
31342
  const secrets = yield* ServerSecretStore;
31254
31343
  const env = environment ?? process.env;
31255
31344
  const fromEnv = fromEnvironment(env);
@@ -31322,7 +31411,7 @@ const make$105 = Effect.fn("HubLink.make")(function* (environment) {
31322
31411
  })
31323
31412
  };
31324
31413
  });
31325
- const layer$89 = Layer.effect(HubLink, make$105());
31414
+ const layer$89 = Layer.effect(HubLink, make$107());
31326
31415
  //#endregion
31327
31416
  //#region src/sync/HubAssetClient.ts
31328
31417
  /**
@@ -31355,7 +31444,7 @@ const decodeAssetListPage = Schema$1.decodeUnknownEffect(AssetListPage);
31355
31444
  const decodeConflictBody$1 = Schema$1.decodeUnknownEffect(ConflictBody$1);
31356
31445
  const decodeAsset = Schema$1.decodeUnknownEffect(AgentAsset);
31357
31446
  var HubAssetClient = class extends Context.Service()("@p4code/cli/sync/HubAssetClient") {};
31358
- const make$104 = Effect.gen(function* () {
31447
+ const make$106 = Effect.gen(function* () {
31359
31448
  const http = yield* HttpClient.HttpClient;
31360
31449
  const link = yield* HubLink;
31361
31450
  const requireSettings = Effect.gen(function* () {
@@ -31437,7 +31526,7 @@ const make$104 = Effect.gen(function* () {
31437
31526
  remove
31438
31527
  };
31439
31528
  });
31440
- const layer$88 = Layer.effect(HubAssetClient, make$104);
31529
+ const layer$88 = Layer.effect(HubAssetClient, make$106);
31441
31530
  //#endregion
31442
31531
  //#region src/sync/mcpRegistrationFiles.ts
31443
31532
  /**
@@ -32368,7 +32457,7 @@ const EMPTY_REPORT = {
32368
32457
  unavailable: null
32369
32458
  };
32370
32459
  var AssetSync = class extends Context.Service()("@p4code/cli/sync/AssetSync") {};
32371
- const make$103 = Effect.gen(function* () {
32460
+ const make$105 = Effect.gen(function* () {
32372
32461
  const client = yield* HubAssetClient;
32373
32462
  const link = yield* HubLink;
32374
32463
  const settingsStore = yield* ServerSettingsService;
@@ -33232,7 +33321,7 @@ const make$103 = Effect.gen(function* () {
33232
33321
  removeLocal
33233
33322
  };
33234
33323
  });
33235
- const layer$87 = Layer.effect(AssetSync, make$103);
33324
+ const layer$87 = Layer.effect(AssetSync, make$105);
33236
33325
  //#endregion
33237
33326
  //#region src/provider/CompressPrompts.ts
33238
33327
  /**
@@ -34621,7 +34710,7 @@ const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
34621
34710
  "interrupted"
34622
34711
  ]);
34623
34712
  var ThreadBackgroundLivenessService = class extends Context.Service()("@p4code/cli/orchestration/ThreadBackgroundLiveness/ThreadBackgroundLivenessService") {};
34624
- function make$102() {
34713
+ function make$104() {
34625
34714
  const stateByThreadId = /* @__PURE__ */ new Map();
34626
34715
  const stateFor = (threadId) => {
34627
34716
  const existing = stateByThreadId.get(threadId);
@@ -34677,7 +34766,7 @@ function make$102() {
34677
34766
  }
34678
34767
  };
34679
34768
  }
34680
- const layer$86 = Layer.effect(ThreadBackgroundLivenessService, Effect.sync(make$102));
34769
+ const layer$86 = Layer.effect(ThreadBackgroundLivenessService, Effect.sync(make$104));
34681
34770
  //#endregion
34682
34771
  //#region ../../packages/shared/src/sourceControl.ts
34683
34772
  const GITHUB_CHANGE_REQUEST_PRESENTATION = {
@@ -41145,12 +41234,12 @@ const runProcessCore = Effect.fn("processRunner.runProcessCore")(function* (spaw
41145
41234
  stderrInvalidUtf8: stderr.invalidUtf8
41146
41235
  };
41147
41236
  });
41148
- const make$101 = Effect.fn("ProcessRunner.make")(function* () {
41237
+ const make$103 = Effect.fn("ProcessRunner.make")(function* () {
41149
41238
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
41150
41239
  const run = (input) => finalizeRunProcess(runProcessCore(spawner, input), input);
41151
41240
  return ProcessRunner.of({ run });
41152
41241
  });
41153
- const layer$85 = Layer.effect(ProcessRunner, make$101());
41242
+ const layer$85 = Layer.effect(ProcessRunner, make$103());
41154
41243
  //#endregion
41155
41244
  //#region src/project/RepositoryIdentityResolver.ts
41156
41245
  const DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY = 512;
@@ -41241,7 +41330,7 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("RepositoryIdentityResol
41241
41330
  rootPath: cacheKey
41242
41331
  }) : null;
41243
41332
  });
41244
- const make$100 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
41333
+ const make$102 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
41245
41334
  const processRunner = yield* ProcessRunner;
41246
41335
  const repositoryIdentityCache = yield* Cache.makeWith((cacheKey) => resolveRepositoryIdentityFromCacheKey(cacheKey).pipe(Effect.provideService(ProcessRunner, processRunner)), {
41247
41336
  capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,
@@ -41256,7 +41345,7 @@ const make$100 = Effect.fn("RepositoryIdentityResolver.make")(function* (options
41256
41345
  });
41257
41346
  return RepositoryIdentityResolver.of({ resolve });
41258
41347
  });
41259
- const layer$84 = Layer.effect(RepositoryIdentityResolver, make$100()).pipe(Layer.provide(layer$85));
41348
+ const layer$84 = Layer.effect(RepositoryIdentityResolver, make$102()).pipe(Layer.provide(layer$85));
41260
41349
  //#endregion
41261
41350
  //#region src/orchestration/Layers/ProjectionSnapshotQuery.ts
41262
41351
  const decodeReadModel = Schema$1.decodeUnknownEffect(OrchestrationReadModel);
@@ -41620,7 +41709,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
41620
41709
  const EvidenceReadContextRow = Schema$1.Struct({
41621
41710
  usedTokensAtRead: Schema$1.NullOr(Schema$1.Number),
41622
41711
  readAt: Schema$1.String,
41623
- afterSequence: Schema$1.NullOr(Schema$1.Number)
41712
+ afterSequence: Schema$1.NullOr(Schema$1.Number),
41713
+ throughSequence: Schema$1.NullOr(Schema$1.Number)
41624
41714
  });
41625
41715
  const getEvidenceReadContextRow = SqlSchema.findOneOption({
41626
41716
  Request: Schema$1.String,
@@ -41633,7 +41723,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
41633
41723
  ELSE NULL
41634
41724
  END AS "usedTokensAtRead",
41635
41725
  created_at AS "readAt",
41636
- json_extract(payload_json, '$.afterSequence') AS "afterSequence"
41726
+ json_extract(payload_json, '$.afterSequence') AS "afterSequence",
41727
+ json_extract(payload_json, '$.throughSequence') AS "throughSequence"
41637
41728
  FROM projection_thread_activities
41638
41729
  WHERE activity_id = ${activityId}
41639
41730
  LIMIT 1
@@ -41747,6 +41838,10 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
41747
41838
  WHERE reads.thread_id = ${threadId}
41748
41839
  AND reads.kind = 'fusion.evidence.read'
41749
41840
  AND json_extract(reads.payload_json, '$.throughSequence') >= 0
41841
+ -- A raw row's bound is wherever paging reached, never the bound a
41842
+ -- wake named, so no report can pair with it and an unpaired one must
41843
+ -- not read as a review nobody answered.
41844
+ AND COALESCE(json_extract(reads.payload_json, '$.raw'), 0) = 0
41750
41845
  ORDER BY reads.created_at DESC, reads.rowid DESC
41751
41846
  LIMIT 1
41752
41847
  `
@@ -43544,7 +43639,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
43544
43639
  threadId,
43545
43640
  turnId
43546
43641
  }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getTurnStart:query", "ProjectionSnapshotQuery.getTurnStart:decodeRow")));
43547
- const getActiveTurnId = (threadId) => getActiveTurnIdRow(threadId).pipe(Effect.map(Option.flatMap((row) => Option.fromNullable(row.activeTurnId))), Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getActiveTurnId:query", "ProjectionSnapshotQuery.getActiveTurnId:decodeRow")));
43642
+ const getActiveTurnId = (threadId) => getActiveTurnIdRow(threadId).pipe(Effect.map(Option.flatMap((row) => Option.fromNullishOr(row.activeTurnId))), Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getActiveTurnId:query", "ProjectionSnapshotQuery.getActiveTurnId:decodeRow")));
43548
43643
  const getLatestThreadMessageWithIdPrefix = (threadId, idPrefix) => getLatestThreadMessageWithIdPrefixRow({
43549
43644
  threadId,
43550
43645
  idPrefix
@@ -44572,7 +44667,7 @@ function mergeWithDefaultKeybindings(custom) {
44572
44667
  * Keybindings - Service tag for keybinding configuration operations.
44573
44668
  */
44574
44669
  var Keybindings = class extends Context.Service()("@p4code/cli/keybindings") {};
44575
- const make$99 = Effect.gen(function* () {
44670
+ const make$101 = Effect.gen(function* () {
44576
44671
  const { keybindingsConfigPath } = yield* ServerConfig$1;
44577
44672
  const fs = yield* FileSystem.FileSystem;
44578
44673
  const path = yield* Path$1.Path;
@@ -44833,7 +44928,7 @@ const make$99 = Effect.gen(function* () {
44833
44928
  }))
44834
44929
  };
44835
44930
  });
44836
- const layer$83 = Layer.effect(Keybindings, make$99);
44931
+ const layer$83 = Layer.effect(Keybindings, make$101);
44837
44932
  //#endregion
44838
44933
  //#region src/process/externalLauncher.ts
44839
44934
  /**
@@ -45060,7 +45155,7 @@ const launchEditorProcess = Effect.fn("externalLauncher.launchEditorProcess")(fu
45060
45155
  cause
45061
45156
  }));
45062
45157
  });
45063
- const make$98 = Effect.gen(function* () {
45158
+ const make$100 = Effect.gen(function* () {
45064
45159
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
45065
45160
  const fileSystem = yield* FileSystem.FileSystem;
45066
45161
  const path = yield* Path$1.Path;
@@ -45071,7 +45166,7 @@ const make$98 = Effect.gen(function* () {
45071
45166
  launchEditor: (input) => provideCommandResolutionServices(Effect.flatMap(resolveEditorLaunch(input), (launch) => launchEditorProcess(launch).pipe(Effect.provideService(ChildProcessSpawner$1.ChildProcessSpawner, spawner))))
45072
45167
  });
45073
45168
  });
45074
- const layer$82 = Layer.effect(ExternalLauncher, make$98);
45169
+ const layer$82 = Layer.effect(ExternalLauncher, make$100);
45075
45170
  //#endregion
45076
45171
  //#region src/orchestration/Services/OrchestrationReactor.ts
45077
45172
  /**
@@ -45089,7 +45184,7 @@ var OrchestrationReactor = class extends Context.Service()("@p4code/cli/orchestr
45089
45184
  //#endregion
45090
45185
  //#region src/serverLifecycleEvents.ts
45091
45186
  var ServerLifecycleEvents = class extends Context.Service()("@p4code/cli/serverLifecycleEvents") {};
45092
- const make$97 = Effect.gen(function* () {
45187
+ const make$99 = Effect.gen(function* () {
45093
45188
  const pubsub = yield* PubSub.unbounded();
45094
45189
  const state = yield* Ref.make({
45095
45190
  sequence: 0,
@@ -45113,7 +45208,7 @@ const make$97 = Effect.gen(function* () {
45113
45208
  }
45114
45209
  };
45115
45210
  });
45116
- const layer$81 = Layer.effect(ServerLifecycleEvents, make$97);
45211
+ const layer$81 = Layer.effect(ServerLifecycleEvents, make$99);
45117
45212
  //#endregion
45118
45213
  //#region src/telemetry/Identify.ts
45119
45214
  const CodexAuthJsonSchema = Schema$1.Struct({ tokens: Schema$1.Struct({ account_id: Schema$1.String }) });
@@ -45286,7 +45381,7 @@ var AnalyticsService = class AnalyticsService extends Context.Service()("@p4code
45286
45381
  /** No-op layer for callers that intentionally disable telemetry. */
45287
45382
  static layerTest = Layer.succeed(AnalyticsService, inert);
45288
45383
  };
45289
- const make$96 = Effect.gen(function* () {
45384
+ const make$98 = Effect.gen(function* () {
45290
45385
  const telemetryConfig = yield* TelemetryEnvConfig;
45291
45386
  const posthogKey = telemetryConfig.posthogKey.trim();
45292
45387
  if (!telemetryConfig.enabled || posthogKey === "") return inert;
@@ -45356,7 +45451,7 @@ const make$96 = Effect.gen(function* () {
45356
45451
  flush
45357
45452
  });
45358
45453
  });
45359
- const layer$80 = Layer.effect(AnalyticsService, make$96);
45454
+ const layer$80 = Layer.effect(AnalyticsService, make$98);
45360
45455
  AnalyticsService.layerTest;
45361
45456
  //#endregion
45362
45457
  //#region src/provider/Services/ProviderSessionDirectory.ts
@@ -45787,7 +45882,7 @@ var BootServiceInstallError = class extends Schema$1.TaggedErrorClass()("BootSer
45787
45882
  }
45788
45883
  };
45789
45884
  var BootService = class extends Context.Service()("@p4code/cli/service/bootService") {};
45790
- const make$95 = Effect.fn("cloud.boot_service.make")(function* (input) {
45885
+ const make$97 = Effect.fn("cloud.boot_service.make")(function* (input) {
45791
45886
  const hostExecPath = yield* HostProcessExecutablePath;
45792
45887
  const hostArguments = yield* HostProcessArguments;
45793
45888
  const host = input.host ?? {
@@ -46009,7 +46104,7 @@ const make$95 = Effect.fn("cloud.boot_service.make")(function* (input) {
46009
46104
  logPath
46010
46105
  });
46011
46106
  });
46012
- const layer$79 = (input) => Layer.effect(BootService, make$95(input));
46107
+ const layer$79 = (input) => Layer.effect(BootService, make$97(input));
46013
46108
  //#endregion
46014
46109
  //#region src/service/selfUpdate.ts
46015
46110
  /**
@@ -46084,7 +46179,7 @@ const resolveServerSelfUpdateCapability = Effect.fn("cloud.server_self_update.re
46084
46179
  return null;
46085
46180
  });
46086
46181
  var ServerSelfUpdate = class extends Context.Service()("@p4code/cli/service/selfUpdate/ServerSelfUpdate") {};
46087
- const make$94 = Effect.fn("cloud.server_self_update.make")(function* (options) {
46182
+ const make$96 = Effect.fn("cloud.server_self_update.make")(function* (options) {
46088
46183
  const serverConfig = yield* ServerConfig$1;
46089
46184
  const settings = yield* ServerSettingsService;
46090
46185
  const continuationContext = yield* Effect.context();
@@ -46247,7 +46342,7 @@ const make$94 = Effect.fn("cloud.server_self_update.make")(function* (options) {
46247
46342
  });
46248
46343
  return ServerSelfUpdate.of({ update });
46249
46344
  });
46250
- const layer$78 = Layer.effect(ServerSelfUpdate, make$94()).pipe(Layer.provide(layer$85));
46345
+ const layer$78 = Layer.effect(ServerSelfUpdate, make$96()).pipe(Layer.provide(layer$85));
46251
46346
  //#endregion
46252
46347
  //#region src/environment/ServerEnvironmentLabel.ts
46253
46348
  const ServerEnvironmentLabelCommandProbe = Schema$1.Literals(["macos-computer-name", "linux-pretty-hostname"]);
@@ -46379,7 +46474,7 @@ function platformArch(architecture) {
46379
46474
  default: return "other";
46380
46475
  }
46381
46476
  }
46382
- const make$93 = Effect.gen(function* () {
46477
+ const make$95 = Effect.gen(function* () {
46383
46478
  const fileSystem = yield* FileSystem.FileSystem;
46384
46479
  const path = yield* Path$1.Path;
46385
46480
  const serverConfig = yield* ServerConfig$1;
@@ -46457,7 +46552,7 @@ const make$93 = Effect.gen(function* () {
46457
46552
  * state. It intentionally has no fallback Layer.succeed value: callers must
46458
46553
  * provide the external platform services and a ServerConfig.
46459
46554
  */
46460
- const layer$77 = Layer.effect(ServerEnvironment, make$93).pipe(Layer.provide(layer$85));
46555
+ const layer$77 = Layer.effect(ServerEnvironment, make$95).pipe(Layer.provide(layer$85));
46461
46556
  //#endregion
46462
46557
  //#region src/sync/SettingsSync.ts
46463
46558
  /**
@@ -46487,7 +46582,9 @@ const IDLE = {
46487
46582
  sent: []
46488
46583
  };
46489
46584
  var SettingsSync = class extends Context.Service()("@p4code/cli/sync/SettingsSync") {};
46490
- const decodePayload = Schema$1.decodeUnknownOption(SettingsSyncPayload);
46585
+ const payloadJson = Schema$1.fromJsonString(SettingsSyncPayload);
46586
+ const decodePayload = Schema$1.decodeUnknownOption(payloadJson);
46587
+ const encodePayload = Schema$1.encodeEffect(payloadJson);
46491
46588
  /** The machine's own name, so history can say where a change came from. */
46492
46589
  const environmentLabel = () => {
46493
46590
  const hostname = NodeOS.hostname().trim();
@@ -46516,7 +46613,7 @@ const promptOverrideIsRunnable = (value) => {
46516
46613
  }
46517
46614
  return true;
46518
46615
  };
46519
- const make$92 = Effect.gen(function* () {
46616
+ const make$94 = Effect.gen(function* () {
46520
46617
  const settingsService = yield* ServerSettingsService;
46521
46618
  const client = yield* HubAssetClient;
46522
46619
  const label = environmentLabel();
@@ -46544,13 +46641,9 @@ const make$92 = Effect.gen(function* () {
46544
46641
  const incoming = Option.flatMap(fetched.asset, (asset) => {
46545
46642
  const file = asset.files.find((candidate) => candidate.path === SETTINGS_SYNC_FILE_PATH);
46546
46643
  if (file === void 0) return Option.none();
46547
- try {
46548
- return decodePayload(JSON.parse(file.content));
46549
- } catch {
46550
- return Option.none();
46551
- }
46644
+ return decodePayload(file.content);
46552
46645
  });
46553
- const now = new Date(yield* Clock.currentTimeMillis).toISOString();
46646
+ const now = DateTime.formatIso(yield* DateTime.now);
46554
46647
  const plan = planSettingsSync({
46555
46648
  local: filterSyncedSettings(settings),
46556
46649
  base: settings.settingsSync.base,
@@ -46604,12 +46697,13 @@ const make$92 = Effect.gen(function* () {
46604
46697
  environmentLabel: label,
46605
46698
  publishedAt: now
46606
46699
  };
46700
+ const content = yield* encodePayload(payload).pipe(Effect.orDie);
46607
46701
  const written = yield* client.write({
46608
46702
  kind: "settings",
46609
46703
  name: ASSET_NAME,
46610
46704
  files: [{
46611
46705
  path: SETTINGS_SYNC_FILE_PATH,
46612
- content: JSON.stringify(payload)
46706
+ content
46613
46707
  }],
46614
46708
  ...Option.match(fetched.asset, {
46615
46709
  onNone: () => ({}),
@@ -46661,7 +46755,7 @@ const make$92 = Effect.gen(function* () {
46661
46755
  unavailable: "settings sync failed"
46662
46756
  })))) };
46663
46757
  });
46664
- const layer$76 = Layer.effect(SettingsSync, make$92);
46758
+ const layer$76 = Layer.effect(SettingsSync, make$94);
46665
46759
  //#endregion
46666
46760
  //#region src/provider/Services/ProviderHealthRefresher.ts
46667
46761
  var ProviderHealthRefresher = class extends Context.Service()("@p4code/cli/provider/Services/ProviderHealthRefresher") {};
@@ -47019,7 +47113,7 @@ function daemonPortLine(line) {
47019
47113
  const port = Number(raw);
47020
47114
  return Number.isInteger(port) && port > 0 && port <= MAX_PORT$1 ? port : null;
47021
47115
  }
47022
- const make$91 = Effect.fn("AgentDeviceProcess.make")(function* () {
47116
+ const make$93 = Effect.fn("AgentDeviceProcess.make")(function* () {
47023
47117
  const fs = yield* FileSystem.FileSystem;
47024
47118
  const path = yield* Path$1.Path;
47025
47119
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
@@ -47131,7 +47225,7 @@ const make$91 = Effect.fn("AgentDeviceProcess.make")(function* () {
47131
47225
  });
47132
47226
  return { start };
47133
47227
  });
47134
- const layer$75 = Layer.effect(AgentDeviceProcess, make$91());
47228
+ const layer$75 = Layer.effect(AgentDeviceProcess, make$93());
47135
47229
  //#endregion
47136
47230
  //#region src/device/DeviceDiscovery.ts
47137
47231
  var DeviceDiscoveryFailure = class extends Data.TaggedError("DeviceDiscoveryFailure") {};
@@ -47164,7 +47258,7 @@ const decodeDeviceDiscovery = (hostId, body) => Schema$1.decodeUnknownEffect(Sch
47164
47258
  partial: (value.errors?.length ?? 0) > 0
47165
47259
  });
47166
47260
  }));
47167
- const make$90 = Effect.fn("DeviceDiscovery.make")(function* () {
47261
+ const make$92 = Effect.fn("DeviceDiscovery.make")(function* () {
47168
47262
  const http = yield* HttpClient.HttpClient;
47169
47263
  return Effect.fn("DeviceDiscovery.read")(function* (input) {
47170
47264
  const origin = yield* Effect.try(() => new URL(input.ready.hub.origin));
@@ -47226,7 +47320,7 @@ const Result = Schema$1.Struct({
47226
47320
  serial: Schema$1.optional(DeviceId),
47227
47321
  error: Schema$1.optional(Schema$1.String)
47228
47322
  });
47229
- const make$89 = Effect.fn("DeviceHubActions.make")(function* () {
47323
+ const make$91 = Effect.fn("DeviceHubActions.make")(function* () {
47230
47324
  const http = yield* HttpClient.HttpClient;
47231
47325
  const post = Effect.fn("DeviceHubActions.post")(function* (ready, route, payload, timeoutMs) {
47232
47326
  return yield* Effect.gen(function* () {
@@ -47295,7 +47389,7 @@ function sessionsToClose(sessions, input) {
47295
47389
  //#endregion
47296
47390
  //#region src/device/DeviceCommandLeases.ts
47297
47391
  /** Owns commands by exact session object, so close/reopen cannot revive an old lease. */
47298
- const make$88 = Effect.fn("DeviceCommandLeases.make")(function* () {
47392
+ const make$90 = Effect.fn("DeviceCommandLeases.make")(function* () {
47299
47393
  const lock = yield* Semaphore.make(1);
47300
47394
  const entries = /* @__PURE__ */ new Map();
47301
47395
  const revoked = /* @__PURE__ */ new WeakSet();
@@ -47341,7 +47435,7 @@ const make$88 = Effect.fn("DeviceCommandLeases.make")(function* () {
47341
47435
  //#endregion
47342
47436
  //#region src/device/DeviceThreadLeases.ts
47343
47437
  /** Thread retirement also cancels opens that have not created a session yet. */
47344
- const make$87 = Effect.fn("DeviceThreadLeases.make")(function* () {
47438
+ const make$89 = Effect.fn("DeviceThreadLeases.make")(function* () {
47345
47439
  const lock = yield* Semaphore.make(1);
47346
47440
  const scopes = /* @__PURE__ */ new Map();
47347
47441
  const blocked = /* @__PURE__ */ new Set();
@@ -47389,7 +47483,7 @@ const make$87 = Effect.fn("DeviceThreadLeases.make")(function* () {
47389
47483
  //#region src/device/DeviceHostRegistry.ts
47390
47484
  const sameConfig = (a, b) => a === b || a !== null && b !== null && a.id === b.id && a.label === b.label && a.target === b.target && a.port === b.port && a.identityFile === b.identityFile;
47391
47485
  /** Owns configuration generations; use() interrupts in-flight work before a host is retired. */
47392
- const make$86 = Effect.fn("DeviceHostRegistry.make")(function* (factories) {
47486
+ const make$88 = Effect.fn("DeviceHostRegistry.make")(function* (factories) {
47393
47487
  const factoryContext = yield* Effect.context();
47394
47488
  const lock = yield* Semaphore.make(1);
47395
47489
  const entries = /* @__PURE__ */ new Map();
@@ -47557,7 +47651,7 @@ function pngDimensions(png) {
47557
47651
  height
47558
47652
  };
47559
47653
  }
47560
- const make$85 = Effect.fn("DeviceScreenshot.make")(function* () {
47654
+ const make$87 = Effect.fn("DeviceScreenshot.make")(function* () {
47561
47655
  const http = yield* HttpClient.HttpClient;
47562
47656
  return Effect.fn("DeviceScreenshot.capture")(function* (ready, session) {
47563
47657
  const origin = yield* Effect.try(() => new URL(ready.hub.origin));
@@ -47584,7 +47678,7 @@ const make$85 = Effect.fn("DeviceScreenshot.make")(function* () {
47584
47678
  })));
47585
47679
  });
47586
47680
  var DeviceScreenshot = class extends Context.Service()("@p4code/cli/device/DeviceScreenshot") {};
47587
- const layer$74 = Layer.effect(DeviceScreenshot, make$85().pipe(Effect.map((capture) => ({ capture }))));
47681
+ const layer$74 = Layer.effect(DeviceScreenshot, make$87().pipe(Effect.map((capture) => ({ capture }))));
47588
47682
  //#endregion
47589
47683
  //#region src/device/XcodeMcpClient.ts
47590
47684
  const XCODE_BUILD_MCP_VERSION = "2.7.0";
@@ -48397,7 +48491,7 @@ const COMMAND_TIMEOUT_MS$1 = 2e4;
48397
48491
  const COMMAND_OUTPUT_LIMIT = 1024 * 1024;
48398
48492
  const COMMAND_FAILURE_CODE = 127;
48399
48493
  const APPLE_DEVICE_MCP_MINIMUM_XCODE = 27;
48400
- const make$84 = Effect.fn("LocalDeviceHost.make")(function* () {
48494
+ const make$86 = Effect.fn("LocalDeviceHost.make")(function* () {
48401
48495
  const config = yield* ServerConfig$1;
48402
48496
  const fs = yield* FileSystem.FileSystem;
48403
48497
  const path = yield* Path$1.Path;
@@ -48550,7 +48644,7 @@ const make$84 = Effect.fn("LocalDeviceHost.make")(function* () {
48550
48644
  stop: lock.withPermit(stop)
48551
48645
  };
48552
48646
  });
48553
- Layer.effect(DeviceHost, make$84()).pipe(Layer.provide(layer$75));
48647
+ Layer.effect(DeviceHost, make$86()).pipe(Layer.provide(layer$75));
48554
48648
  Data.TaggedError("SshHostDiscoveryError");
48555
48649
  var SshInvalidTargetError = class extends Data.TaggedError("SshInvalidTargetError") {};
48556
48650
  var SshCommandError = class extends Data.TaggedError("SshCommandError") {};
@@ -49409,7 +49503,7 @@ const probeSshDeviceHost = Effect.fn("SshDeviceDiagnostics.probe")(function* (co
49409
49503
  //#endregion
49410
49504
  //#region src/device/SshDeviceHost.ts
49411
49505
  const DIAGNOSTICS_CACHE_MS = 1e4;
49412
- const make$83 = Effect.fn("SshDeviceHost.make")(function* (input) {
49506
+ const make$85 = Effect.fn("SshDeviceHost.make")(function* (input) {
49413
49507
  const config = yield* Schema$1.decodeUnknownEffect(SshDeviceHostConfig)({ ...input }).pipe(Effect.mapError(() => new DeviceHostError({
49414
49508
  hostId: input.id,
49415
49509
  step: "configuring SSH device host",
@@ -49549,8 +49643,8 @@ var DeviceService = class extends Context.Service()("@p4code/cli/device/DeviceSe
49549
49643
  const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input) {
49550
49644
  const settings = yield* ServerSettingsService;
49551
49645
  const scope = yield* Scope.Scope;
49552
- const registry = yield* make$86(input);
49553
- const commands = yield* make$88();
49646
+ const registry = yield* make$88(input);
49647
+ const commands = yield* make$90();
49554
49648
  const adapterLeases = /* @__PURE__ */ new Map();
49555
49649
  const closeCommands = (sessions) => Effect.sync(() => {
49556
49650
  for (const session of sessions) {
@@ -49559,7 +49653,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
49559
49653
  adapterLeases.delete(session.leaseId);
49560
49654
  }
49561
49655
  }).pipe(Effect.andThen(commands.close(sessions)));
49562
- const threads = yield* make$87();
49656
+ const threads = yield* make$89();
49563
49657
  const changes = yield* Effect.acquireRelease(PubSub.sliding(STATE_BUFFER_SIZE), PubSub.shutdown);
49564
49658
  const stateLock = yield* Semaphore.make(1);
49565
49659
  const discoveryLock = yield* Semaphore.make(1);
@@ -49970,17 +50064,17 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
49970
50064
  }))
49971
50065
  };
49972
50066
  });
49973
- const make$82 = Effect.gen(function* () {
49974
- const discover = yield* make$90();
49975
- const actions = yield* make$89();
50067
+ const make$84 = Effect.gen(function* () {
50068
+ const discover = yield* make$92();
50069
+ const actions = yield* make$91();
49976
50070
  return yield* makeWithHosts({
49977
- local: make$84(),
49978
- ssh: make$83,
50071
+ local: make$86(),
50072
+ ssh: make$85,
49979
50073
  discover,
49980
50074
  actions
49981
50075
  });
49982
50076
  }).pipe(Effect.provide(layer$75));
49983
- const layer$72 = Layer.effect(DeviceService, make$82);
50077
+ const layer$72 = Layer.effect(DeviceService, make$84);
49984
50078
  //#endregion
49985
50079
  //#region src/serverRuntimeStartup.ts
49986
50080
  var ServerRuntimeStartupError = class extends Schema$1.TaggedErrorClass()("ServerRuntimeStartupError", {
@@ -50125,7 +50219,7 @@ const startProviderRecovery = Effect.fn("startup.startProviderRecovery")(functio
50125
50219
  yield* input.recover;
50126
50220
  yield* input.startReaper;
50127
50221
  });
50128
- const make$81 = Effect.gen(function* () {
50222
+ const make$83 = Effect.gen(function* () {
50129
50223
  const serverConfig = yield* ServerConfig$1;
50130
50224
  const keybindings = yield* Keybindings;
50131
50225
  const orchestrationReactor = yield* OrchestrationReactor;
@@ -50279,7 +50373,7 @@ const make$81 = Effect.gen(function* () {
50279
50373
  enqueueCommand: commandGate.enqueueCommand
50280
50374
  };
50281
50375
  });
50282
- const layer$71 = Layer.effect(ServerRuntimeStartup, make$81);
50376
+ const layer$71 = Layer.effect(ServerRuntimeStartup, make$83);
50283
50377
  //#endregion
50284
50378
  //#region src/serverRuntimeState.ts
50285
50379
  const PersistedServerRuntimeState = Schema$1.Struct({
@@ -50436,7 +50530,7 @@ function expandHomePath$2(input, path) {
50436
50530
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
50437
50531
  return input;
50438
50532
  }
50439
- const make$80 = Effect.gen(function* () {
50533
+ const make$82 = Effect.gen(function* () {
50440
50534
  const fileSystem = yield* FileSystem.FileSystem;
50441
50535
  const path = yield* Path$1.Path;
50442
50536
  const statWorkspaceRoot = Effect.fn("WorkspacePaths.statWorkspaceRoot")(function* (workspaceRoot, normalizedWorkspaceRoot, phase) {
@@ -50493,7 +50587,7 @@ const make$80 = Effect.gen(function* () {
50493
50587
  resolveRelativePathWithinRoot
50494
50588
  });
50495
50589
  });
50496
- const layer$70 = Layer.effect(WorkspacePaths, make$80);
50590
+ const layer$70 = Layer.effect(WorkspacePaths, make$82);
50497
50591
  //#endregion
50498
50592
  //#region src/cli/project.ts
50499
50593
  const isEnvironmentHttpCommonError = Schema$1.is(EnvironmentHttpCommonError);
@@ -54417,7 +54511,7 @@ const makeTerminationError$1 = (handle) => Effect.match(handle.exitCode, {
54417
54511
  //#region ../../packages/effect-acp/src/client.ts
54418
54512
  const MAX_STDERR_CALLBACK_CHARS = 32768;
54419
54513
  var AcpClient = class extends Context.Service()("effect-acp/client/AcpClient") {};
54420
- const make$79 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options = {}, terminationError) {
54514
+ const make$81 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options = {}, terminationError) {
54421
54515
  const coreHandlers = {};
54422
54516
  const notificationHandlers = {
54423
54517
  sessionUpdate: {
@@ -54584,7 +54678,7 @@ const layerChildProcess$1 = (handle, options = {}) => {
54584
54678
  })), Stream.drain)) : stdout
54585
54679
  };
54586
54680
  const terminationError = makeTerminationError$1(handle);
54587
- return Layer.effect(AcpClient, make$79(stdio, options, terminationError));
54681
+ return Layer.effect(AcpClient, make$81(stdio, options, terminationError));
54588
54682
  };
54589
54683
  //#endregion
54590
54684
  //#region src/provider/Drivers/AntigravitySkills.ts
@@ -55863,7 +55957,7 @@ const Reply = Schema$1.Struct({
55863
55957
  id: Schema$1.String,
55864
55958
  result: Schema$1.Struct({ ok: Schema$1.Boolean })
55865
55959
  });
55866
- const make$78 = Effect.fn("DeviceCommandProxy.make")(function* () {
55960
+ const make$80 = Effect.fn("DeviceCommandProxy.make")(function* () {
55867
55961
  const http = yield* HttpClient.HttpClient;
55868
55962
  const daemonSessions = /* @__PURE__ */ new WeakMap();
55869
55963
  return Effect.fn("DeviceCommandProxy.execute")(function* (endpoint, session, payload) {
@@ -55916,7 +56010,7 @@ const make$78 = Effect.fn("DeviceCommandProxy.make")(function* () {
55916
56010
  })));
55917
56011
  });
55918
56012
  var DeviceCommandProxy = class extends Context.Service()("@p4code/cli/device/DeviceCommandProxy") {};
55919
- const layer$69 = Layer.effect(DeviceCommandProxy, make$78().pipe(Effect.map((execute) => ({ execute }))));
56013
+ const layer$69 = Layer.effect(DeviceCommandProxy, make$80().pipe(Effect.map((execute) => ({ execute }))));
55920
56014
  //#endregion
55921
56015
  //#region src/device/DeviceHubRoute.ts
55922
56016
  const DEVICE_HUB_PROXY_PREFIX = "/api/device-hub";
@@ -58265,7 +58359,7 @@ const logP4ProjectFileLoadError = (error) => Effect.logWarning(error).pipe(Effec
58265
58359
  filePath: error.filePath,
58266
58360
  errorTag: error._tag
58267
58361
  }));
58268
- const make$77 = Effect.gen(function* () {
58362
+ const make$79 = Effect.gen(function* () {
58269
58363
  const fileSystem = yield* FileSystem.FileSystem;
58270
58364
  const path = yield* Path$1.Path;
58271
58365
  const load = Effect.fn("P4ProjectFileLoader.load")(function* (workspaceRoot) {
@@ -58286,7 +58380,7 @@ const make$77 = Effect.gen(function* () {
58286
58380
  });
58287
58381
  return P4ProjectFileLoader.of({ load });
58288
58382
  });
58289
- const layer$66 = Layer.effect(P4ProjectFileLoader, make$77);
58383
+ const layer$66 = Layer.effect(P4ProjectFileLoader, make$79);
58290
58384
  //#endregion
58291
58385
  //#region src/project/ProjectFaviconResolver.ts
58292
58386
  /**
@@ -58361,7 +58455,7 @@ function extractIconHref(source) {
58361
58455
  return null;
58362
58456
  }
58363
58457
  const optionOnNotFound$1 = (effect) => effect.pipe(Effect.map(Option.some), Effect.catchTags({ PlatformError: (error) => error.reason._tag === "NotFound" ? Effect.succeed(Option.none()) : Effect.fail(error) }));
58364
- const make$76 = Effect.gen(function* () {
58458
+ const make$78 = Effect.gen(function* () {
58365
58459
  const fileSystem = yield* FileSystem.FileSystem;
58366
58460
  const path = yield* Path$1.Path;
58367
58461
  const workspacePaths = yield* WorkspacePaths;
@@ -58430,7 +58524,7 @@ const make$76 = Effect.gen(function* () {
58430
58524
  });
58431
58525
  return ProjectFaviconResolver.of({ resolvePath });
58432
58526
  });
58433
- const layer$65 = Layer.effect(ProjectFaviconResolver, make$76);
58527
+ const layer$65 = Layer.effect(ProjectFaviconResolver, make$78);
58434
58528
  //#endregion
58435
58529
  //#region src/assets/AssetAccess.ts
58436
58530
  const ASSET_ROUTE_PREFIX = "/api/assets";
@@ -58841,10 +58935,10 @@ const resolveAsset = Effect.fn("AssetAccess.resolveAsset")(function* (token, rel
58841
58935
  //#endregion
58842
58936
  //#region src/observability/BrowserTraceCollector.ts
58843
58937
  var BrowserTraceCollector = class extends Context.Service()("@p4code/cli/observability/BrowserTraceCollector") {};
58844
- const make$75 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
58938
+ const make$77 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
58845
58939
  for (const record of records) sink.push(record);
58846
58940
  }) });
58847
- const layer$64 = (sink) => Layer.succeed(BrowserTraceCollector, make$75(sink));
58941
+ const layer$64 = (sink) => Layer.succeed(BrowserTraceCollector, make$77(sink));
58848
58942
  //#endregion
58849
58943
  //#region src/auth/http.ts
58850
58944
  const CREDENTIAL_RESPONSE_HEADERS = {
@@ -59257,6 +59351,17 @@ function isAutoCheckoutBusy(input) {
59257
59351
  return thread.latestTurn?.state === "running" || thread.session?.status === "starting" || thread.session?.status === "running";
59258
59352
  });
59259
59353
  }
59354
+ /** The brief every advisor gets; Jev reads it as instructions, the model as a question. */
59355
+ const AUTO_WORKSPACE_QUESTION_LINES = (hasWorkingTreeChanges) => [
59356
+ "Choose the workspace for this task before its coding agent starts.",
59357
+ "Return exactly local or worktree in the answer field. Do not perform the task.",
59358
+ "No other P4 agent is currently working in this checkout.",
59359
+ `Uncommitted changes are ${hasWorkingTreeChanges ? "present" : "absent"}.`,
59360
+ "Prefer local for questions, investigation, small focused edits, and work that needs current uncommitted changes.",
59361
+ "Prefer worktree for independent implementation, broad changes, risky experiments, or possible interference with someone else's edits.",
59362
+ "A new worktree starts from the selected base branch; uncommitted files are not copied.",
59363
+ "Honor an explicit workspace preference in the task. Preserve unrelated changes. If uncertain, choose worktree."
59364
+ ];
59260
59365
  const BUSY_CHECKOUT_DECISION = {
59261
59366
  mode: "worktree",
59262
59367
  reason: "Another agent is working in the current checkout."
@@ -59265,27 +59370,23 @@ const UNAVAILABLE_DECISION = {
59265
59370
  mode: "worktree",
59266
59371
  reason: "The agent could not choose a workspace; using an isolated worktree."
59267
59372
  };
59268
- const chooseAutoWorkspace = Effect.fn("chooseAutoWorkspace")(function* (input, textGeneration) {
59373
+ const chooseAutoWorkspace = Effect.fn("chooseAutoWorkspace")(function* (input, textGeneration, advisor) {
59269
59374
  if (!input.isRepo) return {
59270
59375
  mode: "local",
59271
59376
  reason: "This project has no Git repository."
59272
59377
  };
59273
59378
  if (input.busy) return BUSY_CHECKOUT_DECISION;
59379
+ const advised = advisor ? yield* advisor.chooseWorkspace({
59380
+ message: input.message,
59381
+ hasWorkingTreeChanges: input.hasWorkingTreeChanges
59382
+ }) : void 0;
59383
+ if (advised) return advised;
59274
59384
  if (!textGeneration.generateBtwAnswer || !input.modelSelection) return UNAVAILABLE_DECISION;
59275
59385
  return yield* textGeneration.generateBtwAnswer({
59276
59386
  cwd: input.cwd,
59277
59387
  modelSelection: input.modelSelection,
59278
59388
  context: input.message,
59279
- question: [
59280
- "Choose the workspace for this task before its coding agent starts.",
59281
- "Return exactly local or worktree in the answer field. Do not perform the task.",
59282
- "No other P4 agent is currently working in this checkout.",
59283
- `Uncommitted changes are ${input.hasWorkingTreeChanges ? "present" : "absent"}.`,
59284
- "Prefer local for questions, investigation, small focused edits, and work that needs current uncommitted changes.",
59285
- "Prefer worktree for independent implementation, broad changes, risky experiments, or possible interference with someone else's edits.",
59286
- "A new worktree starts from the selected base branch; uncommitted files are not copied.",
59287
- "Honor an explicit workspace preference in the task. Preserve unrelated changes. If uncertain, choose worktree."
59288
- ].join("\n")
59389
+ question: AUTO_WORKSPACE_QUESTION_LINES(input.hasWorkingTreeChanges).join("\n")
59289
59390
  }).pipe(Effect.timeout(AUTO_WORKSPACE_DECISION_TIMEOUT), Effect.map(({ answer }) => {
59290
59391
  const mode = answer.trim().toLowerCase();
59291
59392
  if (mode === "local") return {
@@ -59449,8 +59550,30 @@ const readWorkflowScript = Effect.fn("orchestration.readWorkflowScript")(functio
59449
59550
  * written as if the setting applied only to user-facing answers.
59450
59551
  */
59451
59552
  const FUSION_PAIR_COMMUNICATION_INSTRUCTIONS = `Everything you send the other half of the pair obeys the thread's active response-compression mode, exactly as a user-facing answer does: phase reports, advice, reviews, questions, and handoffs. When the mode is off, write normal prose. Compression governs form only - never drop a requirement, a negation, a number or unit, a file, symbol, or command name, code, security or warning text, the evidence behind a claim, or an exact protocol heading or token in order to save words.`;
59452
- const FUSION_BUILDER_INSTRUCTIONS = `You are Fusion Builder in an already-created native server pair. Server owns pairing and coordination. Follow the current turn's [fusion-review-policy] block when present; it overrides the phase-boundary scheduling rules below. Do not inspect or invoke the Fusion skill, create/pair/rename threads, or announce/setup Fusion. Start the user's task directly. The phase plan is not yours: Supervisor authors it, orders it, and moves every phase status, and it renders in your task banner from there. Never create or edit a phase list yourself - not with TaskCreate, TaskUpdate, TodoWrite, update_plan, the MCP task board tools, or thread_plan_update - and never mark your own phase complete. Work the one phase Supervisor put in progress, and report what you did. Step-level notes for your own use are fine as long as they never become a phase list. Complete exactly one phase per turn, and finish the whole phase in that turn rather than stopping early. Do not run tests, typecheck, lint, or builds per phase; write the tests the change needs, then run verification once in the final phase over the whole task. Exception: a phase whose own correctness is unclear may run the single narrowest check that resolves it. End every phase turn with a report of at most six non-empty lines: the phase you finished, what changed and where, what you verified with its result, and anything unresolved. One line each, every fact once, no restatement of the plan or of advice you were given. Code, exact error text and security warnings are exempt from the cap. Do not start the next phase in the same turn. Server then wakes the paired Supervisor, which resumes you through ${FUSION_ADVICE_PROMPT_PREFIX}; a user message may also revise or resume the work. Final phase verifies the entire task against the original request and labels it ready for whole-task review. Supervisor is unreachable during your turn. Never spawn/use another Supervisor thread/subagent or attribute Supervisor decisions without ${FUSION_ADVICE_PROMPT_PREFIX}. Within the current phase, continue when straightforward or evidence is clear. For a concrete unresolved tradeoff, correctness risk, or design decision materially needing judgment, stop safely before the risky choice; final response states the exact question and why review is needed. Evaluate/follow Supervisor advice unless conflicting with user request or verified repo state. ${FUSION_PAIR_COMMUNICATION_INSTRUCTIONS}`;
59453
- const FUSION_WATCHER_TOOL_INSTRUCTIONS = `Supervisor tools belong to the p4-code MCP server: thread_watch_review, thread_watch_events, thread_evidence_report, thread_plan_update, thread_advise, and thread_gate_respond. If absent from the visible tool list, first use the available tool search/discovery facility to load those exact tools, including provider-prefixed names; absence from the initial list does not establish unavailability. If discovery or a call fails, report the exact missing capability or error and the need to restore the p4-code MCP connection. Do not offer manual relay, draft or implement the task yourself, or abandon the supervisor role as a workaround. For an actionable user requirement, read the relevant builder evidence and relay it through thread_advise; when a gate is open, answer through thread_gate_respond instead. Structure every actionable supervisor advice message with these Markdown headings, in order:
59553
+ /**
59554
+ * What a role does when a tool it wants is not its own.
59555
+ *
59556
+ * Both halves get the same words because the failure is the same on both
59557
+ * sides: a denied capability is an answer about what this role may do, and
59558
+ * treating it as an obstacle to route around turns a boundary the server
59559
+ * enforces into one it merely suggests.
59560
+ */
59561
+ const FUSION_TOOL_BOUNDARY_INSTRUCTIONS = `A tool missing from your role, or a call the server denies for want of a capability or a grant, is a limit to report rather than an obstacle to route around. Never substitute the server's own database, its HTTP API, another MCP server, or any other back channel for a capability you were not granted; say which tool or grant you lacked and what you would have done with it.`;
59562
+ /**
59563
+ * What a role does when the user asks it, in person, for the other half's work.
59564
+ *
59565
+ * Distinct from the tool boundary above: there the role wants a capability and
59566
+ * is told to report the limit, while here the user wants something and the
59567
+ * quiet handover is the failure. A builder that silently parks a tracker
59568
+ * request in its phase report, or a supervisor that silently forwards an
59569
+ * implementation request through thread_advise, answers a question the user
59570
+ * never asked - they may have addressed the wrong half, or may want that half
59571
+ * to say plainly that it cannot. Confirmation costs one turn; a misrouted
59572
+ * instruction costs the work done under it.
59573
+ */
59574
+ const FUSION_OUT_OF_ROLE_REQUEST_INSTRUCTIONS = `A request the user addresses to you that your role does not own is neither yours to perform nor yours to hand to the other half on your own initiative. Name the capability you lack and the half that owns it, ask the user whether they meant it to go there, and end the turn without acting; carry it across only once they confirm. This governs an instruction aimed at you, not a need your own work turned up - something the task itself raised still travels its normal path. A user who already named the other half, or whose instruction only the other half could have been meant for, has confirmed it; do not ask again.`;
59575
+ const FUSION_BUILDER_INSTRUCTIONS = `You are Fusion Builder in an already-created native server pair. Server owns pairing and coordination. Follow the current turn's [fusion-review-policy] block when present; it overrides the phase-boundary scheduling rules below. Do not inspect or invoke the Fusion skill, create/pair/rename threads, or announce/setup Fusion. Start the user's task directly. The phase plan is not yours: Supervisor authors it, orders it, and moves every phase status, and it renders in your task banner from there. Never create or edit a phase list yourself - not with TaskCreate, TaskUpdate, TodoWrite, update_plan, the MCP task board tools, or thread_plan_update - and never mark your own phase complete. Work the one phase Supervisor put in progress, and report what you did. Step-level notes for your own use are fine as long as they never become a phase list. Complete exactly one phase per turn, and finish the whole phase in that turn rather than stopping early. Do not run tests, typecheck, lint, or builds per phase; write the tests the change needs, then run verification once in the final phase over the whole task. Exception: a phase whose own correctness is unclear may run the single narrowest check that resolves it. End every phase turn with a report of at most six non-empty lines: the phase you finished, what changed and where, what you verified with its result, and anything unresolved. One line each, every fact once, no restatement of the plan or of advice you were given. Code, exact error text and security warnings are exempt from the cap. Do not start the next phase in the same turn. Server then wakes the paired Supervisor, which resumes you through ${FUSION_ADVICE_PROMPT_PREFIX}; a user message may also revise or resume the work. Final phase verifies the entire task against the original request and labels it ready for whole-task review. Supervisor is unreachable during your turn. Never spawn/use another Supervisor thread/subagent or attribute Supervisor decisions without ${FUSION_ADVICE_PROMPT_PREFIX}. Within the current phase, continue when straightforward or evidence is clear. For a concrete unresolved tradeoff, correctness risk, or design decision materially needing judgment, stop safely before the risky choice; final response states the exact question and why review is needed. Evaluate/follow Supervisor advice unless conflicting with user request or verified repo state. The review tools are Supervisor's half of the pair, not yours: thread_watch_review, thread_watch_events and thread_evidence_report are its tools, and a thread outside this pair is not yours to read. Your provider may still list them; that is not a grant, and every call fails. The tracker is Supervisor's too: you neither read nor write tickets, so when the work needs one filed, commented, or moved, say so in your phase report and let Supervisor carry it. ${FUSION_OUT_OF_ROLE_REQUEST_INSTRUCTIONS} ${FUSION_TOOL_BOUNDARY_INSTRUCTIONS} ${FUSION_PAIR_COMMUNICATION_INSTRUCTIONS}`;
59576
+ const FUSION_WATCHER_TOOL_INSTRUCTIONS = `Supervisor tools belong to the p4-code MCP server: thread_watch_review, thread_watch_events, thread_evidence_report, thread_plan_update, thread_advise, thread_gate_respond, and the task board tools task_current, task_get, task_list, task_create and task_update. Read the board whenever planning needs it, and read a named ticket with ticket_resolve together with its comments, treating a later comment as newer intent than the description. Resolve which task that is by calling task_current with the builder's threadId, because a pair started from the board links the row to the builder and not to you; a bare task_current answers for your own thread and reports the task as unlinked. Write only within that task: update it or a subtask you created under it, never an unrelated row, never another project's board, and never any board but this one - task_create files a real ticket on an external tracker when given a source such as 'linear', and that is not yours to file. If absent from the visible tool list, first use the available tool search/discovery facility to load those exact tools, including provider-prefixed names; absence from the initial list does not establish unavailability. If discovery or a call fails, report the exact missing capability or error and the need to restore the p4-code MCP connection. Do not offer manual relay, draft or implement the task yourself, or abandon the supervisor role as a workaround. For an actionable user requirement, read the relevant builder evidence and relay it through thread_advise; when a gate is open, answer through thread_gate_respond instead. Structure every actionable supervisor advice message with these Markdown headings, in order:
59454
59577
  ## Recommendation
59455
59578
  State the decision or correction in one sentence. For a phase review, say whether the phase is approved or needs changes.
59456
59579
  ## Reason
@@ -59465,7 +59588,7 @@ Give ordered, concrete actions for the builder, including the next phase and its
59465
59588
  * which is what it did before this existed.
59466
59589
  */
59467
59590
  const FUSION_EVIDENCE_SUMMARY_INSTRUCTIONS = `Read the builder's evidence through a summarizer subagent rather than into your own context: spawn one subagent with the evidence range and have it call thread_watch_review itself, paging with nextAfterSequence against a fixed throughSequence until hasMore is false, and return a summary. Your tool grants reach it, so it can read the paired builder thread exactly as you can. Instruct that subagent that thread_watch_review and thread_watch_events may be absent from its visible tool list and that absence does not establish unavailability: it must first use the available tool search/discovery facility to load those exact tools by name, including provider-prefixed names, and if discovery itself fails it must report that exact error rather than reporting no such tool. Require of that summary: every file path touched with the scale of the change to each, every command run and whether it succeeded or failed, check and test results with their counts, exact error text quoted rather than characterized, the builder's completion claims marked as claims, and anything the builder flagged as uncertain, blocked or unresolved. Over a multi-turn range it must also carry unresolved objections of yours and whether each was answered, phases completed versus reopened, claims never verified, and files touched repeatedly. Every statement cites the sequences behind it so you can check any one of them with a targeted thread_watch_events read. Fail open: if no subagent is available, the spawn errors, it times out, or what comes back is empty or does not cite sequences, read the range yourself and review from the raw evidence. Never skip or shorten a review because a summary was unavailable. Record which fallback you took: a subagent that could not load the watch tools is a misconfiguration worth naming in your report, and it is not the same as a spawn that errored or timed out. Then close the review by calling thread_evidence_report exactly once, after the last page: pass the watched thread id, the same fixed throughSequence the first review page returned - not lastSequence and not the final page's nextAfterSequence, because the server pairs the report to the read by that exact number - the first and last sequence you actually covered, the model you spawned on, and outcome summarized with the subagent's exact returned text, fallback when you read the raw range yourself, or failed when the summarizer could not run or could not report - with the reason in your own words for the last two. Report even when the summary never arrived; a review with no report cannot be told apart from a supervisor that never tried. Never paste builder tool output into it.`;
59468
- const FUSION_WATCHER_INSTRUCTIONS = `You are Fusion Supervisor (watcher) in an already-created native server pair. Follow the current turn's [fusion-review-policy] block when present; it overrides phase scheduling. Server owns pairing and coordination and wakes you with ${FUSION_REVIEW_PROMPT_PREFIX} or ${FUSION_GATE_PROMPT_PREFIX} prompts at builder turn boundaries. A plain message outside such a wake may arrive after your conversational memory of the pair is gone; its [fusion-pair] metadata block is authoritative: the builder thread exists and is the counterpart thread id. Never report that no builder thread exists. For a new pair, the initial user request is yours, and its first turn is research, not relay. Research it: when the request names a ticket, read that ticket AND its comments, including replies and inline comments, and treat a later comment as newer intent than the description; read the code, docs, project instructions, and current state the request touches. Analyse what you found: root cause or the concrete design constraint, scope, what the user actually wants delivered, and which delivery steps they authorized. Only then write the phase plan and direct its first phase. The plan is yours alone: call thread_plan_update with the builder's threadId, sending the complete ordered list every time, each phase named in 3-6 words by its outcome rather than by a command, file path, or flag, with exactly one in progress. Split it into the fewest substantial phases the task genuinely needs plus a final integration/whole-task phase; most tasks need one to three work phases, each a complete reviewable slice of behavior. Never split per file, per function, or per trivial step. State each phase's completion condition to the builder in the advice, not in the banner title. On every later review, rewrite the same plan: an approved phase becomes completed and the next becomes in progress in the same call, an objection leaves the current phase in progress or reopens a phase you had marked completed, and the final approval completes the last phase. The builder never touches it, so a phase you do not move stays where it is. Then direct the phase to the idle builder through thread_advise, stating the findings that make the direction actionable alongside the original requirements, constraints, and authorized delivery scope. Never echo the request back as its own direction, never hand over a plan you did not ground in evidence, and never send ${FUSION_NO_OBJECTION_TEXT} before the builder has received its first phase and produced a turn to review. The builder has not received the initial prompt and must not start before your direction. Missing builder turn evidence is expected before that first direction; do not wait for it or implement the work yourself. Ask the user only when needed to resolve a blocking requirement. ${FUSION_WATCHER_TOOL_INSTRUCTIONS} To resume supervision, read builder evidence with thread_watch_review from lastReviewedImplementerSequence, capture its throughSequence on the first page and reuse that fixed bound while paging with nextAfterSequence until hasMore is false (including empty pages). Apply message append/replace operations by identity; recover truncated evidence through targeted raw thread_watch_events source ranges. Retain reviewed requirements and evidence for final review; recover missing context with targeted history reads rather than mandatory raw replay from zero, derive phase from artifacts (git log/status, PR, builder events, including its turn.plan.updated phase list), steer with thread_advise, and answer an open gate with thread_gate_respond. When a review or gate wake prompt specifies an explicit event range, that range wins over this metadata. Never poll or wait for the builder; deliver review or advice, then end the turn. Every thread_advise starts a builder turn whose completion wakes you again, so never advise a builder that is idle on an external wait or has nothing actionable; report the state in your own thread and end without advising. ${FUSION_PAIR_COMMUNICATION_INSTRUCTIONS}`;
59591
+ const FUSION_WATCHER_INSTRUCTIONS = `You are Fusion Supervisor (watcher) in an already-created native server pair. Follow the current turn's [fusion-review-policy] block when present; it overrides phase scheduling. Server owns pairing and coordination and wakes you with ${FUSION_REVIEW_PROMPT_PREFIX} or ${FUSION_GATE_PROMPT_PREFIX} prompts at builder turn boundaries. A plain message outside such a wake may arrive after your conversational memory of the pair is gone; its [fusion-pair] metadata block is authoritative: the builder thread exists and is the counterpart thread id. Never report that no builder thread exists. For a new pair, the initial user request is yours, and its first turn is research, not relay. Research it: when the request names a ticket, read that ticket AND its comments, including replies and inline comments, and treat a later comment as newer intent than the description; read the code, docs, project instructions, and current state the request touches. Analyse what you found: root cause or the concrete design constraint, scope, what the user actually wants delivered, and which delivery steps they authorized. Only then write the phase plan and direct its first phase. The plan is yours alone: call thread_plan_update with the builder's threadId, sending the complete ordered list every time, each phase named in 3-6 words by its outcome rather than by a command, file path, or flag, with exactly one in progress. Split it into the fewest substantial phases the task genuinely needs plus a final integration/whole-task phase; most tasks need one to three work phases, each a complete reviewable slice of behavior. Never split per file, per function, or per trivial step. State each phase's completion condition to the builder in the advice, not in the banner title. On every later review, rewrite the same plan: an approved phase becomes completed and the next becomes in progress in the same call, an objection leaves the current phase in progress or reopens a phase you had marked completed, and the final approval completes the last phase. The builder never touches it, so a phase you do not move stays where it is. Then direct the phase to the idle builder through thread_advise, stating the findings that make the direction actionable alongside the original requirements, constraints, and authorized delivery scope. Never echo the request back as its own direction, never hand over a plan you did not ground in evidence, and never send ${FUSION_NO_OBJECTION_TEXT} before the builder has received its first phase and produced a turn to review. The builder has not received the initial prompt and must not start before your direction. Missing builder turn evidence is expected before that first direction; do not wait for it or implement the work yourself. Ask the user only when needed to resolve a blocking requirement. ${FUSION_WATCHER_TOOL_INSTRUCTIONS} To resume supervision, read builder evidence with thread_watch_review from lastReviewedImplementerSequence, capture its throughSequence on the first page and reuse that fixed bound while paging with nextAfterSequence until hasMore is false (including empty pages). Apply message append/replace operations by identity; recover truncated evidence through targeted raw thread_watch_events source ranges. Retain reviewed requirements and evidence for final review; recover missing context with targeted history reads rather than mandatory raw replay from zero, derive phase from artifacts (git log/status, PR, builder events, including its turn.plan.updated phase list), steer with thread_advise, and answer an open gate with thread_gate_respond. When a review or gate wake prompt specifies an explicit event range, that range wins over this metadata. Never poll or wait for the builder; deliver review or advice, then end the turn. Every thread_advise starts a builder turn whose completion wakes you again, so never advise a builder that is idle on an external wait or has nothing actionable; report the state in your own thread and end without advising. ${FUSION_OUT_OF_ROLE_REQUEST_INSTRUCTIONS} ${FUSION_TOOL_BOUNDARY_INSTRUCTIONS} ${FUSION_PAIR_COMMUNICATION_INSTRUCTIONS}`;
59469
59592
  /**
59470
59593
  * The one-line stand-in for the full block on a message whose session already
59471
59594
  * carries the role instructions.
@@ -59625,16 +59748,33 @@ const FUSION_SAFETY_KERNEL = [
59625
59748
  "- Never commit, push, or open a pull request unless the current turn asked for it, and never commit to main or master directly.",
59626
59749
  "- Never add AI attribution to a commit message, pull request, issue, comment, code, or document."
59627
59750
  ].join("\n");
59628
- /** Everything the supervisor half needs from the P4 MCP server, and nothing else. */
59751
+ /**
59752
+ * Everything the supervisor half needs from the P4 MCP server, and nothing else.
59753
+ *
59754
+ * The task tools are here because planning is the supervisor's job, not an
59755
+ * aside to it: it owns the phase plan, so it has to be able to read the board,
59756
+ * break the work down and keep the authorized task honest. Mutation stays
59757
+ * bounded by the prompt rather than by this set, which can only say whether a
59758
+ * tool exists - the rule is the current task, never someone else's board row.
59759
+ * `task_propose` is deliberately absent: it tells its caller to stop and wait
59760
+ * for the user, and a supervisor that waits is one the builder sits idle
59761
+ * behind. Asking the user is what `ask_user_question` is for.
59762
+ */
59629
59763
  const FUSION_SUPERVISOR_P4_TOOLS = /* @__PURE__ */ new Set([
59630
59764
  "thread_watch_review",
59631
59765
  "thread_watch_events",
59632
59766
  "thread_evidence_report",
59633
59767
  "thread_advise",
59634
59768
  "thread_gate_respond",
59769
+ "thread_pair_detach",
59635
59770
  "thread_plan_update",
59636
59771
  "ask_user_question",
59637
- "task_current"
59772
+ "task_current",
59773
+ "task_get",
59774
+ "task_list",
59775
+ "task_create",
59776
+ "task_update",
59777
+ "ticket_resolve"
59638
59778
  ]);
59639
59779
  /**
59640
59780
  * What the builder half must not reach.
@@ -59642,15 +59782,23 @@ const FUSION_SUPERVISOR_P4_TOOLS = /* @__PURE__ */ new Set([
59642
59782
  * Framed as a denial rather than an allowance because the builder's job is
59643
59783
  * open-ended: it needs coding, verification and preview tools, and a new one of
59644
59784
  * those should reach it without an edit here. What it must never have is the
59645
- * pair's own machinery - spawning threads, creating or detaching pairs,
59646
- * advising, answering its own gate - the phase plan, which the supervisor
59647
- * owns outright, plus task-board mutation and the external services that
59648
- * belong to a person rather than to an implementation turn.
59785
+ * pair's own machinery - spawning threads, creating pairs, advising, answering
59786
+ * its own gate - the phase plan, which the supervisor owns outright, plus
59787
+ * task-board mutation and the external services that belong to a person rather
59788
+ * than to an implementation turn. The review tools join that list: they read
59789
+ * the review of the builder's own work, which is the supervisor watching the
59790
+ * builder and never the other way round.
59791
+ *
59792
+ * Both roles retain `thread_pair_detach` as their way out. Its handler limits
59793
+ * detaching to a pair containing the caller whose other thread this session
59794
+ * created. The role policy exposes the action; the handler enforces ownership.
59649
59795
  */
59650
59796
  const FUSION_BUILDER_DENIED_P4_TOOLS = /* @__PURE__ */ new Set([
59651
59797
  "thread_spawn",
59798
+ "thread_watch_review",
59799
+ "thread_watch_events",
59800
+ "thread_evidence_report",
59652
59801
  "thread_pair_create",
59653
- "thread_pair_detach",
59654
59802
  "thread_cleanup",
59655
59803
  "thread_rename",
59656
59804
  "thread_configure",
@@ -59695,21 +59843,90 @@ function fusionContextPolicyFor(role, optimizedContext) {
59695
59843
  optimizedContext
59696
59844
  };
59697
59845
  }
59698
- /** Whether the lean profile applies at all. Ordinary threads never reach it. */
59846
+ /**
59847
+ * Whether a role may call one P4 tool at all, regardless of any setting.
59848
+ *
59849
+ * The hiding functions below are a context measure: they keep a role's tool
59850
+ * list short, they only work on adapters that expose a per-session filter, and
59851
+ * the user can turn them off. This is the authorization question instead, so
59852
+ * it takes no setting and no adapter - a role that cannot act on a tool must
59853
+ * not be able to call it on any provider, whether or not the list it was shown
59854
+ * mentioned it.
59855
+ *
59856
+ * Shaped from the same two sets the hiding uses, so the list a role sees and
59857
+ * the calls it may make cannot drift apart. A thread with no role is not half
59858
+ * of a pair and is not this function's business.
59859
+ */
59860
+ function fusionRoleMayCallP4Tool(role, toolName) {
59861
+ if (role === void 0) return true;
59862
+ return role === "watcher" ? FUSION_SUPERVISOR_P4_TOOLS.has(toolName) : !FUSION_BUILDER_DENIED_P4_TOOLS.has(toolName);
59863
+ }
59864
+ /**
59865
+ * Whether the context trimming applies. Ordinary threads never reach it.
59866
+ *
59867
+ * Only the measures that exist to save tokens ask this: the P4 tool list and
59868
+ * the setting sources. What a role may do is not a token question and must not
59869
+ * consult a setting the user can turn off - see `fusionRoleApplies`.
59870
+ */
59699
59871
  function fusionLeanContextApplies(input) {
59700
59872
  return input.role !== void 0 && input.optimizedContext;
59701
59873
  }
59702
59874
  /**
59703
- * The external MCP registrations a Fusion session keeps: none.
59875
+ * Whether a role's restrictions apply: whenever there is a role.
59876
+ *
59877
+ * Kept apart from the trimming above because the two answer different
59878
+ * questions. Turning context optimization off should cost a pair its token
59879
+ * savings, never hand the builder the tracker and the planning tools back or
59880
+ * give the supervisor an editor.
59881
+ */
59882
+ function fusionRoleApplies(input) {
59883
+ return input.role !== void 0;
59884
+ }
59885
+ /**
59886
+ * The registered servers the supervisor half keeps, by lowercased name.
59704
59887
  *
59705
- * Everything a user registered for their own work goes, because neither role
59706
- * can act on it during a pair turn and every definition is paid for on every
59707
- * turn of the loop. Computer Use needs no exception here - it is a
59888
+ * Only the tracker, and only for the supervisor. It researches the request
59889
+ * before it plans, and `ticket_resolve` answers with a ticket's description
59890
+ * alone - the comments that routinely carry newer intent are reachable only
59891
+ * through the user's own Linear registration. The builder keeps none: it
59892
+ * implements the phase it was given and has no errand on a tracker.
59893
+ *
59894
+ * The name is repeated here rather than imported from `mcp/LinearMcpClient`,
59895
+ * which every adapter would then pull into a provider process. A test pins
59896
+ * the two together, exactly as the P4 tool catalogue is pinned.
59897
+ */
59898
+ const FUSION_SUPERVISOR_EXTERNAL_MCP_SERVERS = /* @__PURE__ */ new Set(["linear"]);
59899
+ /**
59900
+ * The name a registration was given, with the user-scope prefix taken back off.
59901
+ *
59902
+ * A server the user registered in their own provider config reaches a session
59903
+ * re-keyed as `p4-user-<name>`, so matching the raw key would look for a name
59904
+ * the registry never hands over and would drop the tracker it was written to
59905
+ * keep. The prefix is spelled here for the same reason the name is: importing
59906
+ * the registry would pull it into every provider process.
59907
+ */
59908
+ const USER_SCOPE_REGISTRATION_PREFIX = "p4-user-";
59909
+ const fusionRegistrationBareName = (name) => {
59910
+ const lowered = name.toLowerCase();
59911
+ return lowered.startsWith(USER_SCOPE_REGISTRATION_PREFIX) ? lowered.slice(8) : lowered;
59912
+ };
59913
+ /**
59914
+ * The external MCP registrations a Fusion session keeps.
59915
+ *
59916
+ * Applies to any pair turn, optimized or not: which servers a role may reach
59917
+ * is authorization, and the setting only governs what is trimmed to save
59918
+ * tokens.
59919
+ *
59920
+ * Everything else a user registered for their own work goes, because neither
59921
+ * role can act on it during a pair turn and every definition is paid for on
59922
+ * every turn of the loop. Computer Use needs no exception here - it is a
59708
59923
  * provider-owned endpoint attached only to a session that was granted it, not
59709
59924
  * a registration in this map, so a granted session keeps it either way.
59710
59925
  */
59711
59926
  function fusionExternalMcpServers(servers, input) {
59712
- return fusionLeanContextApplies(input) ? {} : { ...servers };
59927
+ if (!fusionRoleApplies(input)) return { ...servers };
59928
+ if (input.role !== "watcher") return {};
59929
+ return Object.fromEntries(Object.entries(servers).filter(([name]) => FUSION_SUPERVISOR_EXTERNAL_MCP_SERVERS.has(fusionRegistrationBareName(name))));
59713
59930
  }
59714
59931
  /**
59715
59932
  * P4 tools this role must not see, given the full catalogue.
@@ -59723,20 +59940,70 @@ function fusionHiddenP4Tools(catalogue, input) {
59723
59940
  return input.role === "watcher" ? catalogue.filter((name) => !FUSION_SUPERVISOR_P4_TOOLS.has(name)) : catalogue.filter((name) => FUSION_BUILDER_DENIED_P4_TOOLS.has(name));
59724
59941
  }
59725
59942
  /**
59943
+ * Provider-native tools the supervisor must not see.
59944
+ *
59945
+ * It reviews and directs; it never edits the tree. Reading tools stay, because
59946
+ * grounding an objection in the code is exactly its job, and a shell stays too
59947
+ * - `git log` and `git status` are how it derives phase from artifacts. What
59948
+ * goes is the means to write the change it is supposed to be reviewing.
59949
+ *
59950
+ * Named per provider, like the builder's list, because the names are the
59951
+ * provider's rather than ours.
59952
+ */
59953
+ const FUSION_SUPERVISOR_DENIED_NATIVE_TOOLS = [
59954
+ "Edit",
59955
+ "MultiEdit",
59956
+ "Write",
59957
+ "NotebookEdit",
59958
+ "apply_patch"
59959
+ ];
59960
+ /**
59726
59961
  * Provider-native tools this role must not see.
59727
59962
  *
59728
59963
  * The supervisor keeps its own planning tool: it is the half that plans, and
59729
- * for its own thread that tool is the ordinary one.
59964
+ * for its own thread that tool is the ordinary one. It loses the editing tools
59965
+ * instead, which are the builder's half of the same split.
59966
+ *
59967
+ * Applies to any pair turn, optimized or not, for the same reason the external
59968
+ * registrations do: a builder holding the planning tool is a role problem, not
59969
+ * a token one.
59730
59970
  */
59731
59971
  function fusionHiddenNativeTools(input) {
59732
- return fusionLeanContextApplies(input) && input.role === "implementer" ? FUSION_BUILDER_DENIED_NATIVE_TOOLS : [];
59972
+ if (!fusionRoleApplies(input)) return [];
59973
+ return input.role === "implementer" ? FUSION_BUILDER_DENIED_NATIVE_TOOLS : FUSION_SUPERVISOR_DENIED_NATIVE_TOOLS;
59974
+ }
59975
+ /**
59976
+ * Global agents a pair still owes its user after the lean profile drops the
59977
+ * user setting source.
59978
+ *
59979
+ * Dropping that source is what makes the profile lean, but it takes the user's
59980
+ * subagent definitions with it, and the delivery loop this pair runs on names
59981
+ * one of them outright: the supervisor will not accept the work until the
59982
+ * global `pr-reviewer` has reviewed the branch. Leaving it unregistered means
59983
+ * the builder either cannot comply or quietly reviews with something else,
59984
+ * which is the failure this list exists to prevent. Named rather than
59985
+ * discovered, so the cost stays one definition instead of every agent the user
59986
+ * happens to have.
59987
+ */
59988
+ const FUSION_REQUIRED_GLOBAL_AGENTS = ["pr-reviewer"];
59989
+ /**
59990
+ * Which global agents this session has to register for itself.
59991
+ *
59992
+ * Empty unless the lean profile actually dropped the user source: when it is
59993
+ * off the provider loads them the usual way, and registering them again would
59994
+ * be a second definition of the same agent.
59995
+ */
59996
+ function fusionRequiredGlobalAgents(input) {
59997
+ return fusionLeanContextApplies(input) ? FUSION_REQUIRED_GLOBAL_AGENTS : [];
59733
59998
  }
59734
59999
  /**
59735
60000
  * Setting sources a Fusion session reads.
59736
60001
  *
59737
60002
  * `user` carries the global memory files and the user-level skills; the
59738
60003
  * project's own instructions live in `project` and `local` and stay, because
59739
- * they are the ones that describe the code being changed.
60004
+ * they are the ones that describe the code being changed. What the drop also
60005
+ * takes, and what `fusionRequiredGlobalAgents` above hands back, is the user's
60006
+ * subagent definitions.
59740
60007
  */
59741
60008
  function fusionSettingSources(sources, input) {
59742
60009
  if (!fusionLeanContextApplies(input)) return sources;
@@ -60303,7 +60570,7 @@ const defaultCancelTimeout = Duration.seconds(15);
60303
60570
  const defaultSessionLoadTimeout = Duration.seconds(90);
60304
60571
  const defaultSessionLoadReplayIdleGap = Duration.seconds(2);
60305
60572
  var AcpSessionRuntime = class extends Context.Service()("@p4code/cli/provider/acp/AcpSessionRuntime") {};
60306
- const make$74 = (options) => Effect.gen(function* () {
60573
+ const make$76 = (options) => Effect.gen(function* () {
60307
60574
  const crypto = yield* Crypto.Crypto;
60308
60575
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
60309
60576
  const runtimeScope = yield* Scope.Scope;
@@ -60707,7 +60974,7 @@ const make$74 = (options) => Effect.gen(function* () {
60707
60974
  notify: acp.raw.notify
60708
60975
  };
60709
60976
  });
60710
- const layer$63 = (options) => Layer.effect(AcpSessionRuntime, make$74(options));
60977
+ const layer$63 = (options) => Layer.effect(AcpSessionRuntime, make$76(options));
60711
60978
  function sessionConfigOptionsFromSetup(response) {
60712
60979
  return response?.configOptions ?? [];
60713
60980
  }
@@ -65165,7 +65432,7 @@ const BundledOnlyModelManifest = {
65165
65432
  refreshInBackground: Effect.void
65166
65433
  };
65167
65434
  Layer.succeed(ModelManifest, BundledOnlyModelManifest);
65168
- const make$73 = Effect.gen(function* () {
65435
+ const make$75 = Effect.gen(function* () {
65169
65436
  const fileSystem = yield* FileSystem.FileSystem;
65170
65437
  const path = yield* Path$1.Path;
65171
65438
  const config = yield* ServerConfig$1;
@@ -65210,7 +65477,7 @@ const make$73 = Effect.gen(function* () {
65210
65477
  refreshInBackground: Effect.forkIn(guardedRefresh, serviceScope).pipe(Effect.asVoid)
65211
65478
  });
65212
65479
  });
65213
- const layer$62 = Layer.effect(ModelManifest, make$73);
65480
+ const layer$62 = Layer.effect(ModelManifest, make$75);
65214
65481
  //#endregion
65215
65482
  //#region src/provider/ProviderDriver.ts
65216
65483
  function defaultProviderContinuationIdentity(input) {
@@ -65476,11 +65743,11 @@ const makeTextGenerationFromRegistry = (registry) => TextGeneration.of({
65476
65743
  detail: "This provider does not report account usage."
65477
65744
  }))))
65478
65745
  });
65479
- const make$72 = Effect.gen(function* () {
65746
+ const make$74 = Effect.gen(function* () {
65480
65747
  const registry = yield* ProviderInstanceRegistry;
65481
65748
  return makeTextGenerationFromRegistry(registry);
65482
65749
  });
65483
- const layer$61 = Layer.effect(TextGeneration, make$72);
65750
+ const layer$61 = Layer.effect(TextGeneration, make$74);
65484
65751
  //#endregion
65485
65752
  //#region src/provider/Layers/claudeUsageLimits.ts
65486
65753
  const SESSION_MINS$1 = 300;
@@ -69927,6 +70194,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
69927
70194
  optimizedContext: fusionPromptSettings?.optimizedContext ?? DEFAULT_FUSION_PROMPT_SETTINGS.optimizedContext
69928
70195
  };
69929
70196
  const externalMcpServers = fusionExternalMcpServers(registeredMcpServers, fusionContextPolicy);
70197
+ const requiredGlobalAgents = yield* discoverClaudeUserAgents(claudeSettings, fusionRequiredGlobalAgents(fusionContextPolicy), claudeEnvironment, input.cwd).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path$1.Path, path));
69930
70198
  const hiddenTools = [...fusionHiddenP4Tools(providerMcpToolNames(), fusionContextPolicy).map((name) => `mcp__${P4CODE_MCP_SERVER_NAME}__${name}`), ...fusionHiddenNativeTools(fusionContextPolicy)];
69931
70199
  const unpromptedSubagents = input.unpromptedSubagents !== void 0 ? input.unpromptedSubagents : options?.resolveUnpromptedSubagents === void 0 ? DEFAULT_SERVER_SETTINGS.enableUnpromptedSubagents : yield* options.resolveUnpromptedSubagents;
69932
70200
  const compressRuleset = compressRulesetFor(input.compressMode ?? "off");
@@ -69958,6 +70226,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
69958
70226
  ...systemPromptAppend.length > 0 ? { append: systemPromptAppend } : {}
69959
70227
  },
69960
70228
  settingSources: [...fusionSettingSources(CLAUDE_SETTING_SOURCES, fusionContextPolicy)],
70229
+ ...Object.keys(requiredGlobalAgents).length > 0 ? { agents: requiredGlobalAgents } : {},
69961
70230
  ...hiddenTools.length > 0 ? { disallowedTools: hiddenTools } : {},
69962
70231
  ...effectiveEffort ? { effort: effectiveEffort } : {},
69963
70232
  ...permissionMode ? { permissionMode } : {},
@@ -88447,7 +88716,7 @@ const makeTerminationError = (handle) => Effect.match(handle.exitCode, {
88447
88716
  //#endregion
88448
88717
  //#region ../../packages/effect-codex-app-server/src/client.ts
88449
88718
  var CodexAppServerClient = class extends Context.Service()("effect-codex-app-server/client/CodexAppServerClient") {};
88450
- const make$71 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(function* (stdio, options = {}, terminationError) {
88719
+ const make$73 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(function* (stdio, options = {}, terminationError) {
88451
88720
  const requestHandlers = /* @__PURE__ */ new Map();
88452
88721
  const notificationHandlers = /* @__PURE__ */ new Map();
88453
88722
  let unknownRequestHandler;
@@ -88514,7 +88783,7 @@ const make$71 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(f
88514
88783
  const layerChildProcess = (handle, options = {}) => Layer.effect(CodexAppServerClient, makeChildProcessClient(handle, options));
88515
88784
  const makeChildProcessClient = Effect.fn("effect-codex-app-server/CodexAppServerClient.makeChildProcessClient")(function* (handle, options) {
88516
88785
  yield* Stream.runDrain(handle.stderr).pipe(Effect.ignore, Effect.forkScoped);
88517
- return yield* make$71(makeChildStdio(handle), options, makeTerminationError(handle));
88786
+ return yield* make$73(makeChildStdio(handle), options, makeTerminationError(handle));
88518
88787
  });
88519
88788
  const resolveCodexLaunchArgs = (launchArgs, environment = process.env) => environment["P4CODE_CODEX_LAUNCH_ARGS"]?.trim() || launchArgs?.trim() || "";
88520
88789
  const codexLaunchArgv = (launchArgs) => tokenizeCliArgs(launchArgs);
@@ -95673,7 +95942,7 @@ function mapMuseExecRecord(input) {
95673
95942
  const toolName = state.toolNameByTaskId.get(taskId);
95674
95943
  state.settledToolTaskIds.add(taskId);
95675
95944
  const failed = stringField$1(recordField(payload, "correlation_facts"), "outcome") === "error";
95676
- const detail = truncate(stringField$1(payload, "text"), MAX_TOOL_DETAIL_CHARS);
95945
+ const detail = truncate$1(stringField$1(payload, "text"), MAX_TOOL_DETAIL_CHARS);
95677
95946
  const editFacts = recordField(payload, "edit_facts");
95678
95947
  return [(stamp) => ({
95679
95948
  type: "item.completed",
@@ -95697,7 +95966,7 @@ function mapMuseExecRecord(input) {
95697
95966
  const toolName = state.toolNameByTaskId.get(taskId);
95698
95967
  if (!toolName || state.settledToolTaskIds.has(taskId)) return [];
95699
95968
  state.settledToolTaskIds.add(taskId);
95700
- const detail = truncate(stringField$1(event, "reason"), MAX_TOOL_DETAIL_CHARS);
95969
+ const detail = truncate$1(stringField$1(event, "reason"), MAX_TOOL_DETAIL_CHARS);
95701
95970
  return [(stamp) => ({
95702
95971
  type: "item.completed",
95703
95972
  ...stamp,
@@ -95874,7 +96143,7 @@ function recordField(source, key) {
95874
96143
  function eventField(payload) {
95875
96144
  return recordField(payload, "event");
95876
96145
  }
95877
- function truncate(value, max) {
96146
+ function truncate$1(value, max) {
95878
96147
  if (value === void 0) return void 0;
95879
96148
  const trimmed = value.trim();
95880
96149
  if (trimmed.length === 0) return void 0;
@@ -100275,8 +100544,8 @@ const makeWithReader = (api) => Effect.gen(function* () {
100275
100544
  }))
100276
100545
  };
100277
100546
  });
100278
- const make$70 = makeCliproxyApi.pipe(Effect.flatMap(makeWithReader));
100279
- const layer$60 = Layer.effect(UsageLimitSources, make$70);
100547
+ const make$72 = makeCliproxyApi.pipe(Effect.flatMap(makeWithReader));
100548
+ const layer$60 = Layer.effect(UsageLimitSources, make$72);
100280
100549
  //#endregion
100281
100550
  //#region src/resourceTelemetry/HostResources.ts
100282
100551
  const CPU_SAMPLE_INTERVAL = "200 millis";
@@ -100311,7 +100580,7 @@ function darwinAvailableMemory(output) {
100311
100580
  const available = (Number(free) + Number(inactive) + Number(speculative)) * Number(pageSize);
100312
100581
  return Number.isSafeInteger(available) && Number(pageSize) > 0 ? available : null;
100313
100582
  }
100314
- const make$69 = Effect.fn("makeHostResources")(function* () {
100583
+ const make$71 = Effect.fn("makeHostResources")(function* () {
100315
100584
  const fs = yield* FileSystem.FileSystem;
100316
100585
  const platform = yield* HostProcessPlatform;
100317
100586
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
@@ -100345,7 +100614,7 @@ const make$69 = Effect.fn("makeHostResources")(function* () {
100345
100614
  });
100346
100615
  return HostResources.of({ read: Cache.get(cache, "host") });
100347
100616
  });
100348
- const layer$59 = Layer.effect(HostResources, make$69());
100617
+ const layer$59 = Layer.effect(HostResources, make$71());
100349
100618
  //#endregion
100350
100619
  //#region src/resourceTelemetry/DesktopTelemetryReceiver.ts
100351
100620
  const INITIAL_SAMPLE_DEADLINE_MS = 9e4;
@@ -100480,7 +100749,7 @@ function requireDesktopTelemetryWriteProgress(fd, remainingBytes, written) {
100480
100749
  }));
100481
100750
  }
100482
100751
  /** @public Service construction is part of the canonical Effect module API. */
100483
- const make$68 = Effect.fn("resourceTelemetry.desktopTelemetryReceiver.make")(function* () {
100752
+ const make$70 = Effect.fn("resourceTelemetry.desktopTelemetryReceiver.make")(function* () {
100484
100753
  const serverConfig = yield* ServerConfig$1;
100485
100754
  const hasDescriptors = serverConfig.mode === "desktop" && serverConfig.desktopTelemetryFd === 4 && serverConfig.desktopTelemetryControlFd === 5;
100486
100755
  const config = {
@@ -100637,7 +100906,7 @@ const make$68 = Effect.fn("resourceTelemetry.desktopTelemetryReceiver.make")(fun
100637
100906
  setDiagnosticsDemand
100638
100907
  });
100639
100908
  });
100640
- const layer$58 = Layer.effect(DesktopTelemetryReceiver, make$68());
100909
+ const layer$58 = Layer.effect(DesktopTelemetryReceiver, make$70());
100641
100910
  //#endregion
100642
100911
  //#region src/resourceTelemetry/Model.ts
100643
100912
  const MAX_DELTA_INTERVAL_MS = 3e4;
@@ -101062,7 +101331,7 @@ function resourceMonitorRustTarget(platform, architecture, linuxLibc) {
101062
101331
  }
101063
101332
  if (platform === "win32") return architecture === "arm64" ? "aarch64-pc-windows-msvc" : architecture === "x64" ? "x86_64-pc-windows-msvc" : void 0;
101064
101333
  }
101065
- const make$67 = Effect.fn("resourceTelemetry.resourceMonitorBinary.make")(function* () {
101334
+ const make$69 = Effect.fn("resourceTelemetry.resourceMonitorBinary.make")(function* () {
101066
101335
  const fileSystem = yield* FileSystem.FileSystem;
101067
101336
  const path = yield* Path$1.Path;
101068
101337
  const platform = yield* HostProcessPlatform;
@@ -101108,7 +101377,7 @@ const make$67 = Effect.fn("resourceTelemetry.resourceMonitorBinary.make")(functi
101108
101377
  });
101109
101378
  return ResourceMonitorBinary.of({ resolve });
101110
101379
  });
101111
- const layer$57 = Layer.effect(ResourceMonitorBinary, make$67());
101380
+ const layer$57 = Layer.effect(ResourceMonitorBinary, make$69());
101112
101381
  //#endregion
101113
101382
  //#region src/resourceTelemetry/NativeTelemetryClient.ts
101114
101383
  const SAMPLE_INTERVAL_MS$1 = 1e3;
@@ -101258,7 +101527,7 @@ function canCommandNativeTelemetrySidecar(status, hasHandle) {
101258
101527
  return hasHandle && (status === "healthy" || status === "degraded");
101259
101528
  }
101260
101529
  /** @public Service construction is part of the canonical Effect module API. */
101261
- const make$66 = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(function* () {
101530
+ const make$68 = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(function* () {
101262
101531
  const binary = yield* ResourceMonitorBinary;
101263
101532
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
101264
101533
  const crypto = yield* Crypto.Crypto;
@@ -101669,7 +101938,7 @@ const make$66 = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(functi
101669
101938
  })
101670
101939
  });
101671
101940
  });
101672
- const layer$56 = Layer.effect(NativeTelemetryClient, make$66());
101941
+ const layer$56 = Layer.effect(NativeTelemetryClient, make$68());
101673
101942
  //#endregion
101674
101943
  //#region src/resourceTelemetry/ResourceAttribution.ts
101675
101944
  var ResourceAttribution = class extends Context.Service()("@p4code/cli/resourceTelemetry/ResourceAttribution") {};
@@ -101681,7 +101950,7 @@ function nonNegativeInteger(value, fallback) {
101681
101950
  if (!Number.isFinite(value)) return 0;
101682
101951
  return Math.max(0, Math.round(value));
101683
101952
  }
101684
- const make$65 = Effect.fn("resourceTelemetry.resourceAttribution.make")(function* () {
101953
+ const make$67 = Effect.fn("resourceTelemetry.resourceAttribution.make")(function* () {
101685
101954
  const entries = yield* Ref.make(/* @__PURE__ */ new Map());
101686
101955
  const record = (input) => Ref.update(entries, (current) => {
101687
101956
  const next = new Map(current);
@@ -101707,7 +101976,7 @@ const make$65 = Effect.fn("resourceTelemetry.resourceAttribution.make")(function
101707
101976
  })
101708
101977
  });
101709
101978
  });
101710
- const layer$55 = Layer.effect(ResourceAttribution, make$65());
101979
+ const layer$55 = Layer.effect(ResourceAttribution, make$67());
101711
101980
  //#endregion
101712
101981
  //#region src/resourceTelemetry/ResourceTelemetryHistory.ts
101713
101982
  const MAX_HISTORY_WINDOW_MS = 60 * 6e4;
@@ -101930,7 +102199,7 @@ function buildHealth(input) {
101930
102199
  };
101931
102200
  }
101932
102201
  /** @public Service construction is part of the canonical Effect module API. */
101933
- const make$64 = Effect.fn("resourceTelemetry.resourceTelemetry.make")(function* () {
102202
+ const make$66 = Effect.fn("resourceTelemetry.resourceTelemetry.make")(function* () {
101934
102203
  const nativeClient = yield* NativeTelemetryClient;
101935
102204
  const desktopReceiver = yield* DesktopTelemetryReceiver;
101936
102205
  const attribution = yield* ResourceAttribution;
@@ -102182,7 +102451,7 @@ const make$64 = Effect.fn("resourceTelemetry.resourceTelemetry.make")(function*
102182
102451
  })))
102183
102452
  });
102184
102453
  });
102185
- const layer$54 = Layer.effect(ResourceTelemetry, make$64());
102454
+ const layer$54 = Layer.effect(ResourceTelemetry, make$66());
102186
102455
  //#endregion
102187
102456
  //#region src/device/DeviceInspection.ts
102188
102457
  const READ_TIMEOUT_MS = 1e4;
@@ -105337,7 +105606,7 @@ const classifyNonZeroExit = (command, stderr) => {
105337
105606
  if (command === "gh" && (normalized.includes("could not resolve to a pullrequest") || normalized.includes("repository.pullrequest") || normalized.includes("no pull requests found for branch") || normalized.includes("pull request not found")) || command === "glab" && (normalized.includes("merge request not found") || normalized.includes("not found") || normalized.includes("404")) || command === "az" && normalized.includes("pull request") && (normalized.includes("not found") || normalized.includes("does not exist"))) return "not-found";
105338
105607
  return "command-failed";
105339
105608
  };
105340
- const make$63 = Effect.gen(function* () {
105609
+ const make$65 = Effect.gen(function* () {
105341
105610
  const processRunner = yield* ProcessRunner;
105342
105611
  const run = Effect.fn("VcsProcess.run")(function* (input) {
105343
105612
  const baseError = {
@@ -105396,7 +105665,7 @@ const make$63 = Effect.gen(function* () {
105396
105665
  });
105397
105666
  return VcsProcess.of({ run });
105398
105667
  });
105399
- const layer$53 = Layer.effect(VcsProcess, make$63).pipe(Layer.provide(layer$85));
105668
+ const layer$53 = Layer.effect(VcsProcess, make$65).pipe(Layer.provide(layer$85));
105400
105669
  //#endregion
105401
105670
  //#region src/vcs/VcsDriver.ts
105402
105671
  var VcsDriver = class extends Context.Service()("@p4code/cli/vcs/VcsDriver") {};
@@ -105847,12 +106116,12 @@ const makeVcsDriver = Effect.gen(function* () {
105847
106116
  const driver = yield* makeVcsDriverShape();
105848
106117
  return VcsDriver.of(driver);
105849
106118
  });
105850
- const make$62 = Effect.gen(function* () {
106119
+ const make$64 = Effect.gen(function* () {
105851
106120
  const git = yield* makeGitVcsDriverCore();
105852
106121
  return GitVcsDriver.of(git);
105853
106122
  });
105854
106123
  Layer.effect(VcsDriver, makeVcsDriver);
105855
- const layer$52 = Layer.effect(GitVcsDriver, make$62);
106124
+ const layer$52 = Layer.effect(GitVcsDriver, make$64);
105856
106125
  //#endregion
105857
106126
  //#region src/vcs/VcsProjectConfig.ts
105858
106127
  const ProjectVcsConfigJson = fromLenientJson(Schema$1.Struct({
@@ -105884,7 +106153,7 @@ const logVcsProjectConfigError = (error) => Effect.logWarning(error).pipe(Effect
105884
106153
  configPath: error.configPath,
105885
106154
  errorTag: error._tag
105886
106155
  }));
105887
- const make$61 = Effect.gen(function* () {
106156
+ const make$63 = Effect.gen(function* () {
105888
106157
  const fileSystem = yield* FileSystem.FileSystem;
105889
106158
  const path = yield* Path$1.Path;
105890
106159
  const findConfigPath = Effect.fn("VcsProjectConfig.findConfigPath")(function* (cwd) {
@@ -105925,7 +106194,7 @@ const make$61 = Effect.gen(function* () {
105925
106194
  });
105926
106195
  return VcsProjectConfig.of({ resolveKind });
105927
106196
  });
105928
- const layer$51 = Layer.effect(VcsProjectConfig, make$61);
106197
+ const layer$51 = Layer.effect(VcsProjectConfig, make$63);
105929
106198
  //#endregion
105930
106199
  //#region src/vcs/VcsDriverRegistry.ts
105931
106200
  const DETECTION_CACHE_CAPACITY = 2048;
@@ -105945,7 +106214,7 @@ function parseDetectionCacheKey(key) {
105945
106214
  cwd: key.slice(separatorIndex + 1)
105946
106215
  };
105947
106216
  }
105948
- const make$60 = Effect.gen(function* () {
106217
+ const make$62 = Effect.gen(function* () {
105949
106218
  const projectConfig = yield* VcsProjectConfig;
105950
106219
  const git = yield* makeVcsDriver;
105951
106220
  const drivers = { git };
@@ -106002,7 +106271,7 @@ const make$60 = Effect.gen(function* () {
106002
106271
  resolve
106003
106272
  });
106004
106273
  });
106005
- const layer$50 = Layer.effect(VcsDriverRegistry, make$60).pipe(Layer.provide(layer$51));
106274
+ const layer$50 = Layer.effect(VcsDriverRegistry, make$62).pipe(Layer.provide(layer$51));
106006
106275
  //#endregion
106007
106276
  //#region src/checkpointing/CheckpointStore.ts
106008
106277
  /**
@@ -106022,7 +106291,7 @@ const layer$50 = Layer.effect(VcsDriverRegistry, make$60).pipe(Layer.provide(lay
106022
106291
  */
106023
106292
  /** Service tag for checkpoint persistence and restore operations. */
106024
106293
  var CheckpointStore = class extends Context.Service()("@p4code/cli/checkpointing/CheckpointStore") {};
106025
- const make$59 = Effect.gen(function* () {
106294
+ const make$61 = Effect.gen(function* () {
106026
106295
  const vcsRegistry = yield* VcsDriverRegistry;
106027
106296
  const resolveCheckpoints = Effect.fn("CheckpointStore.resolveCheckpoints")(function* (operation, cwd) {
106028
106297
  const handle = yield* vcsRegistry.resolve({ cwd });
@@ -106061,7 +106330,7 @@ const make$59 = Effect.gen(function* () {
106061
106330
  deleteCheckpointRefs
106062
106331
  });
106063
106332
  });
106064
- const layer$49 = Layer.effect(CheckpointStore, make$59);
106333
+ const layer$49 = Layer.effect(CheckpointStore, make$61);
106065
106334
  //#endregion
106066
106335
  //#region src/checkpointing/CheckpointDiffQuery.ts
106067
106336
  /**
@@ -106083,7 +106352,7 @@ function buildTurnDiffResult(input, diff) {
106083
106352
  diff
106084
106353
  };
106085
106354
  }
106086
- const make$58 = Effect.gen(function* () {
106355
+ const make$60 = Effect.gen(function* () {
106087
106356
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
106088
106357
  const checkpointStore = yield* CheckpointStore;
106089
106358
  const threadActivities = yield* ProjectionThreadActivityRepository;
@@ -106254,7 +106523,7 @@ const make$58 = Effect.gen(function* () {
106254
106523
  getFullThreadDiff
106255
106524
  });
106256
106525
  });
106257
- const layer$48 = Layer.effect(CheckpointDiffQuery, make$58);
106526
+ const layer$48 = Layer.effect(CheckpointDiffQuery, make$60);
106258
106527
  //#endregion
106259
106528
  //#region src/orchestration/ThreadLiveEventCoalescer.ts
106260
106529
  const COALESCE_WINDOW = Duration.millis(50);
@@ -106799,7 +107068,7 @@ const serversEqual = (left, right) => {
106799
107068
  }
106800
107069
  return true;
106801
107070
  };
106802
- const make$57 = Effect.gen(function* PortDiscoveryMake() {
107071
+ const make$59 = Effect.gen(function* PortDiscoveryMake() {
106803
107072
  const net = yield* NetService;
106804
107073
  const processRunner = yield* ProcessRunner;
106805
107074
  const hostPlatform = yield* HostProcessPlatform;
@@ -106950,7 +107219,7 @@ const make$57 = Effect.gen(function* PortDiscoveryMake() {
106950
107219
  unregisterTerminal
106951
107220
  });
106952
107221
  }).pipe(Effect.withSpan("PortDiscovery.make"));
106953
- const layer$47 = Layer.effect(PortDiscovery, make$57);
107222
+ const layer$47 = Layer.effect(PortDiscovery, make$59);
106954
107223
  //#endregion
106955
107224
  //#region src/terminal/Manager.ts
106956
107225
  /**
@@ -107628,7 +107897,7 @@ function normalizedRuntimeEnv(env) {
107628
107897
  if (entries.length === 0) return null;
107629
107898
  return Object.fromEntries(entries.toSorted(([left], [right]) => left.localeCompare(right)));
107630
107899
  }
107631
- const make$56 = Effect.fn("TerminalManager.make")(function* () {
107900
+ const make$58 = Effect.fn("TerminalManager.make")(function* () {
107632
107901
  const { terminalLogsDir } = yield* ServerConfig$1;
107633
107902
  const ptyAdapter = yield* PtyAdapter;
107634
107903
  const portDiscovery = yield* PortDiscovery;
@@ -108590,7 +108859,7 @@ const makeWithOptions$1 = Effect.fn("TerminalManager.makeWithOptions")(function*
108590
108859
  subscribeMetadata
108591
108860
  });
108592
108861
  });
108593
- const layer$46 = Layer.effect(TerminalManager, make$56()).pipe(Layer.provide(layer$85));
108862
+ const layer$46 = Layer.effect(TerminalManager, make$58()).pipe(Layer.provide(layer$85));
108594
108863
  //#endregion
108595
108864
  //#region src/project/ProjectSetupScriptRunner.ts
108596
108865
  var ProjectSetupScriptOperationError = class extends Schema$1.TaggedErrorClass()("ProjectSetupScriptOperationError", {
@@ -108622,7 +108891,7 @@ var ProjectSetupScriptProjectNotFoundError = class extends Schema$1.TaggedErrorC
108622
108891
  };
108623
108892
  Schema$1.Union([ProjectSetupScriptOperationError, ProjectSetupScriptProjectNotFoundError]);
108624
108893
  var ProjectSetupScriptRunner = class extends Context.Service()("@p4code/cli/project/ProjectSetupScriptRunner") {};
108625
- const make$55 = Effect.gen(function* () {
108894
+ const make$57 = Effect.gen(function* () {
108626
108895
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
108627
108896
  const terminalManager = yield* TerminalManager;
108628
108897
  const settingsService = yield* Effect.serviceOption(ServerSettingsService);
@@ -108685,7 +108954,7 @@ const make$55 = Effect.gen(function* () {
108685
108954
  });
108686
108955
  return ProjectSetupScriptRunner.of({ runForThread });
108687
108956
  });
108688
- const layer$45 = Layer.effect(ProjectSetupScriptRunner, make$55);
108957
+ const layer$45 = Layer.effect(ProjectSetupScriptRunner, make$57);
108689
108958
  //#endregion
108690
108959
  //#region src/sourceControl/azureDevOpsPullRequests.ts
108691
108960
  const AzureDevOpsPullRequestSchema = Schema$1.Struct({
@@ -109013,7 +109282,7 @@ function decodeAzureDevOpsJson(raw, schema, operation, cwd) {
109013
109282
  cause
109014
109283
  })));
109015
109284
  }
109016
- const make$54 = Effect.gen(function* () {
109285
+ const make$56 = Effect.gen(function* () {
109017
109286
  const process = yield* VcsProcess;
109018
109287
  const execute = (input) => process.run({
109019
109288
  operation: "AzureDevOpsCli.execute",
@@ -109155,7 +109424,7 @@ const make$54 = Effect.gen(function* () {
109155
109424
  }).pipe(Effect.asVoid)
109156
109425
  });
109157
109426
  });
109158
- const layer$44 = Layer.effect(AzureDevOpsCli, make$54);
109427
+ const layer$44 = Layer.effect(AzureDevOpsCli, make$56);
109159
109428
  //#endregion
109160
109429
  //#region src/sourceControl/SourceControlProviderDiscovery.ts
109161
109430
  function firstNonEmptyLine(text) {
@@ -109359,7 +109628,7 @@ function toChangeRequest$5(summary) {
109359
109628
  isCrossRepository: false
109360
109629
  };
109361
109630
  }
109362
- const make$53 = Effect.gen(function* () {
109631
+ const make$55 = Effect.gen(function* () {
109363
109632
  const azure = yield* AzureDevOpsCli;
109364
109633
  return SourceControlProvider.of({
109365
109634
  kind: "azure-devops",
@@ -109452,7 +109721,7 @@ const make$53 = Effect.gen(function* () {
109452
109721
  })))
109453
109722
  });
109454
109723
  });
109455
- Layer.effect(SourceControlProvider, make$53);
109724
+ Layer.effect(SourceControlProvider, make$55);
109456
109725
  //#endregion
109457
109726
  //#region src/sourceControl/bitbucketPullRequests.ts
109458
109727
  const BitbucketRepositoryRefSchema = Schema$1.Struct({
@@ -109829,7 +110098,7 @@ function responseError(operation, response) {
109829
110098
  responseBodyLength: collected.text.length
109830
110099
  }))));
109831
110100
  }
109832
- const make$52 = Effect.gen(function* () {
110101
+ const make$54 = Effect.gen(function* () {
109833
110102
  const config = yield* BitbucketApiEnvConfig;
109834
110103
  const httpClient = yield* HttpClient.HttpClient;
109835
110104
  const fileSystem = yield* FileSystem.FileSystem;
@@ -110045,7 +110314,7 @@ const make$52 = Effect.gen(function* () {
110045
110314
  })))
110046
110315
  });
110047
110316
  });
110048
- const layer$42 = Layer.effect(BitbucketApi, make$52);
110317
+ const layer$42 = Layer.effect(BitbucketApi, make$54);
110049
110318
  //#endregion
110050
110319
  //#region src/sourceControl/BitbucketSourceControlProvider.ts
110051
110320
  function toChangeRequest$4(summary) {
@@ -110063,7 +110332,7 @@ function toChangeRequest$4(summary) {
110063
110332
  ...summary.headRepositoryOwnerLogin !== void 0 ? { headRepositoryOwnerLogin: summary.headRepositoryOwnerLogin } : {}
110064
110333
  };
110065
110334
  }
110066
- const make$51 = Effect.gen(function* () {
110335
+ const make$53 = Effect.gen(function* () {
110067
110336
  const bitbucket = yield* BitbucketApi;
110068
110337
  return SourceControlProvider.of({
110069
110338
  kind: "bitbucket",
@@ -110154,7 +110423,7 @@ const make$51 = Effect.gen(function* () {
110154
110423
  })))
110155
110424
  });
110156
110425
  });
110157
- Layer.effect(SourceControlProvider, make$51);
110426
+ Layer.effect(SourceControlProvider, make$53);
110158
110427
  const makeDiscovery = Effect.gen(function* () {
110159
110428
  return {
110160
110429
  type: "api",
@@ -110402,7 +110671,7 @@ function deriveRepositoryCloneUrlsFromCreateOutput(stdout, repository) {
110402
110671
  sshUrl: `git@${fallbackHost}:${repository}.git`
110403
110672
  };
110404
110673
  }
110405
- const make$50 = Effect.gen(function* () {
110674
+ const make$52 = Effect.gen(function* () {
110406
110675
  const process = yield* VcsProcess;
110407
110676
  const execute = (input) => process.run({
110408
110677
  operation: "GitHubCli.execute",
@@ -110520,7 +110789,7 @@ const make$50 = Effect.gen(function* () {
110520
110789
  }).pipe(Effect.asVoid)
110521
110790
  });
110522
110791
  });
110523
- const layer$40 = Layer.effect(GitHubCli, make$50);
110792
+ const layer$40 = Layer.effect(GitHubCli, make$52);
110524
110793
  //#endregion
110525
110794
  //#region src/sourceControl/gitHubAuthStatus.ts
110526
110795
  const GitHubAuthStatusAccountSchema = Schema$1.Struct({
@@ -110622,7 +110891,7 @@ const discovery$1 = {
110622
110891
  parseAuth: parseGitHubAuth,
110623
110892
  installHint: "Install the GitHub command-line tool (`gh`) via https://cli.github.com/ or your package manager (for example `brew install gh`)."
110624
110893
  };
110625
- const make$49 = Effect.gen(function* () {
110894
+ const make$51 = Effect.gen(function* () {
110626
110895
  const github = yield* GitHubCli;
110627
110896
  const listChangeRequests = (input) => {
110628
110897
  if (input.state === "open") return github.listOpenPullRequests({
@@ -110738,7 +111007,7 @@ const make$49 = Effect.gen(function* () {
110738
111007
  })))
110739
111008
  });
110740
111009
  });
110741
- Layer.effect(SourceControlProvider, make$49);
111010
+ Layer.effect(SourceControlProvider, make$51);
110742
111011
  //#endregion
110743
111012
  //#region src/sourceControl/gitLabMergeRequests.ts
110744
111013
  const GitLabProjectReferenceSchema = Schema$1.Struct({
@@ -111057,7 +111326,7 @@ function parseRepositoryPath(repository) {
111057
111326
  projectPath
111058
111327
  };
111059
111328
  }
111060
- const make$48 = Effect.gen(function* () {
111329
+ const make$50 = Effect.gen(function* () {
111061
111330
  const process = yield* VcsProcess;
111062
111331
  const run = (input, mapError) => process.run({
111063
111332
  operation: "GitLabCli.execute",
@@ -111208,7 +111477,7 @@ const make$48 = Effect.gen(function* () {
111208
111477
  }).pipe(Effect.asVoid)
111209
111478
  });
111210
111479
  });
111211
- const layer$38 = Layer.effect(GitLabCli, make$48);
111480
+ const layer$38 = Layer.effect(GitLabCli, make$50);
111212
111481
  //#endregion
111213
111482
  //#region src/sourceControl/gitLabAuthStatus.ts
111214
111483
  const HOST_LINE_PATTERN = /^(?:[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?|\[[a-f0-9:.]+\])(?::\d+)?$/iu;
@@ -111306,7 +111575,7 @@ const discovery = {
111306
111575
  refineUnknownRemote: refineUnknownGitLabRemote,
111307
111576
  installHint: "Install the GitLab command-line tool (`glab`) from https://gitlab.com/gitlab-org/cli or your package manager (for example `brew install glab`)."
111308
111577
  };
111309
- const make$47 = Effect.gen(function* () {
111578
+ const make$49 = Effect.gen(function* () {
111310
111579
  const gitlab = yield* GitLabCli;
111311
111580
  return SourceControlProvider.of({
111312
111581
  kind: "gitlab",
@@ -111395,7 +111664,7 @@ const make$47 = Effect.gen(function* () {
111395
111664
  })))
111396
111665
  });
111397
111666
  });
111398
- Layer.effect(SourceControlProvider, make$47);
111667
+ Layer.effect(SourceControlProvider, make$49);
111399
111668
  //#endregion
111400
111669
  //#region src/sourceControl/SourceControlProviderRegistry.ts
111401
111670
  const PROVIDER_DETECTION_CACHE_CAPACITY = 2048;
@@ -111551,12 +111820,12 @@ const makeWithProviders = Effect.fn("makeSourceControlProviderRegistryWithProvid
111551
111820
  })), { concurrency: "unbounded" })
111552
111821
  });
111553
111822
  });
111554
- const make$46 = Effect.gen(function* () {
111555
- const github = yield* make$49;
111556
- const gitlab = yield* make$47;
111557
- const bitbucket = yield* make$51;
111823
+ const make$48 = Effect.gen(function* () {
111824
+ const github = yield* make$51;
111825
+ const gitlab = yield* make$49;
111826
+ const bitbucket = yield* make$53;
111558
111827
  const bitbucketDiscovery = yield* makeDiscovery;
111559
- const azureDevOps = yield* make$53;
111828
+ const azureDevOps = yield* make$55;
111560
111829
  return yield* makeWithProviders([
111561
111830
  {
111562
111831
  kind: "github",
@@ -111580,7 +111849,7 @@ const make$46 = Effect.gen(function* () {
111580
111849
  }
111581
111850
  ]);
111582
111851
  });
111583
- const layer$36 = Layer.effect(SourceControlProviderRegistry, make$46);
111852
+ const layer$36 = Layer.effect(SourceControlProviderRegistry, make$48);
111584
111853
  //#endregion
111585
111854
  //#region src/sourceControl/PrTemplateDetection.ts
111586
111855
  const TEMPLATE_MAX_BYTES = 8e3;
@@ -111951,7 +112220,7 @@ function toPullRequestHeadRemoteInfo(pr) {
111951
112220
  ...pr.headRepositoryOwnerLogin !== void 0 ? { headRepositoryOwnerLogin: pr.headRepositoryOwnerLogin } : {}
111952
112221
  };
111953
112222
  }
111954
- const make$45 = Effect.gen(function* () {
112223
+ const make$47 = Effect.gen(function* () {
111955
112224
  const gitCore = yield* GitVcsDriver;
111956
112225
  const sourceControlProviders = yield* SourceControlProviderRegistry;
111957
112226
  const textGeneration = yield* TextGeneration;
@@ -112925,7 +113194,7 @@ const make$45 = Effect.gen(function* () {
112925
113194
  runStackedAction
112926
113195
  });
112927
113196
  });
112928
- const layer$35 = Layer.effect(GitManager, make$45);
113197
+ const layer$35 = Layer.effect(GitManager, make$47);
112929
113198
  //#endregion
112930
113199
  //#region src/git/GitWorkflowService.ts
112931
113200
  var GitWorkflowService = class extends Context.Service()("@p4code/cli/git/GitWorkflowService") {};
@@ -112962,7 +113231,7 @@ function nonRepositoryListRefs() {
112962
113231
  totalCount: 0
112963
113232
  };
112964
113233
  }
112965
- const make$44 = Effect.gen(function* () {
113234
+ const make$46 = Effect.gen(function* () {
112966
113235
  const registry = yield* VcsDriverRegistry;
112967
113236
  const git = yield* GitVcsDriver;
112968
113237
  const gitManager = yield* GitManager;
@@ -113048,7 +113317,7 @@ const make$44 = Effect.gen(function* () {
113048
113317
  renameBranch: (input) => ensureGit("GitWorkflowService.renameBranch", input.cwd).pipe(Effect.andThen(git.renameBranch(input)))
113049
113318
  });
113050
113319
  });
113051
- const layer$34 = Layer.effect(GitWorkflowService, make$44);
113320
+ const layer$34 = Layer.effect(GitWorkflowService, make$46);
113052
113321
  //#endregion
113053
113322
  //#region src/pullRequest/PullRequestProvider.ts
113054
113323
  /**
@@ -113409,7 +113678,7 @@ function isReviewerName(value) {
113409
113678
  const name = value.trim();
113410
113679
  return name.length > 0 && !name.startsWith("-");
113411
113680
  }
113412
- const make$43 = Effect.gen(function* () {
113681
+ const make$45 = Effect.gen(function* () {
113413
113682
  const azure = yield* AzureDevOpsCli;
113414
113683
  const detectArgs = ["--detect", "true"];
113415
113684
  const executeJson = (input) => azure.execute({
@@ -113609,7 +113878,7 @@ const make$43 = Effect.gen(function* () {
113609
113878
  }).pipe(Effect.asVoid)
113610
113879
  });
113611
113880
  });
113612
- const layer$33 = Layer.effect(AzureDevOpsPullRequestCli, make$43);
113881
+ const layer$33 = Layer.effect(AzureDevOpsPullRequestCli, make$45);
113613
113882
  //#endregion
113614
113883
  //#region src/pullRequest/AzureDevOpsPullRequestProvider.ts
113615
113884
  const CAPABILITIES$3 = {
@@ -113684,7 +113953,7 @@ function toChangeRequest$1(pullRequest) {
113684
113953
  labels: []
113685
113954
  };
113686
113955
  }
113687
- const make$42 = Effect.gen(function* () {
113956
+ const make$44 = Effect.gen(function* () {
113688
113957
  const cli = yield* AzureDevOpsPullRequestCli;
113689
113958
  const fail = (operation) => (error) => new PullRequestProviderError({
113690
113959
  provider: "azure-devops",
@@ -114424,7 +114693,7 @@ function mergeStrategy(method) {
114424
114693
  default: return "merge_commit";
114425
114694
  }
114426
114695
  }
114427
- const make$41 = Effect.gen(function* () {
114696
+ const make$43 = Effect.gen(function* () {
114428
114697
  const bitbucket = yield* BitbucketApi;
114429
114698
  /**
114430
114699
  * The repository's own path, and the workspace above it — which the people who may review are
@@ -114704,7 +114973,7 @@ const make$41 = Effect.gen(function* () {
114704
114973
  }).pipe(Effect.asVoid))
114705
114974
  });
114706
114975
  });
114707
- const layer$32 = Layer.effect(BitbucketPullRequestApi, make$41);
114976
+ const layer$32 = Layer.effect(BitbucketPullRequestApi, make$43);
114708
114977
  //#endregion
114709
114978
  //#region src/pullRequest/BitbucketPullRequestProvider.ts
114710
114979
  const CAPABILITIES$2 = {
@@ -114784,7 +115053,7 @@ function toChangeRequest(pullRequest) {
114784
115053
  labels: []
114785
115054
  };
114786
115055
  }
114787
- const make$40 = Effect.gen(function* () {
115056
+ const make$42 = Effect.gen(function* () {
114788
115057
  const api = yield* BitbucketPullRequestApi;
114789
115058
  const fail = (operation) => (error) => new PullRequestProviderError({
114790
115059
  provider: "bitbucket",
@@ -117013,7 +117282,7 @@ function actionArgs$1(action, mergeMethod, updateMethod) {
117013
117282
  case "reopen": return ["reopen"];
117014
117283
  }
117015
117284
  }
117016
- const make$39 = Effect.gen(function* () {
117285
+ const make$41 = Effect.gen(function* () {
117017
117286
  const github = yield* GitHubCli;
117018
117287
  /**
117019
117288
  * The pull request's own node id, which is what a mutation against the pull request itself is
@@ -117776,7 +118045,7 @@ const make$39 = Effect.gen(function* () {
117776
118045
  })))
117777
118046
  });
117778
118047
  });
117779
- const layer$31 = Layer.effect(GitHubPullRequestCli, make$39);
118048
+ const layer$31 = Layer.effect(GitHubPullRequestCli, make$41);
117780
118049
  //#endregion
117781
118050
  //#region src/pullRequest/GitHubPullRequestProvider.ts
117782
118051
  const CAPABILITIES$1 = {
@@ -117893,7 +118162,7 @@ function loginAvatarUrl(login, host) {
117893
118162
  }
117894
118163
  /** True where markdown would render nothing: whitespace, or only HTML comments. */
117895
118164
  const rendersEmpty = (body) => body.replace(/<!--[\s\S]*?-->/g, "").trim().length === 0;
117896
- const make$38 = Effect.gen(function* () {
118165
+ const make$40 = Effect.gen(function* () {
117897
118166
  const cli = yield* GitHubPullRequestCli;
117898
118167
  const fail = (operation) => (error) => new PullRequestProviderError({
117899
118168
  provider: "github",
@@ -118915,7 +119184,7 @@ function actionArgs(action, mergeMethod) {
118915
119184
  case "reopen": return ["reopen"];
118916
119185
  }
118917
119186
  }
118918
- const make$37 = Effect.gen(function* () {
119187
+ const make$39 = Effect.gen(function* () {
118919
119188
  const gitlab = yield* GitLabCli;
118920
119189
  const api = (input) => gitlab.execute({
118921
119190
  cwd: input.cwd,
@@ -119486,7 +119755,7 @@ const make$37 = Effect.gen(function* () {
119486
119755
  }).pipe(Effect.asVoid)
119487
119756
  });
119488
119757
  });
119489
- const layer$30 = Layer.effect(GitLabPullRequestCli, make$37);
119758
+ const layer$30 = Layer.effect(GitLabPullRequestCli, make$39);
119490
119759
  //#endregion
119491
119760
  //#region src/pullRequest/GitLabPullRequestProvider.ts
119492
119761
  const CAPABILITIES = {
@@ -119566,7 +119835,7 @@ function reasonFor(error) {
119566
119835
  if (error._tag === "GitLabCliAuthenticationError") return "unauthenticated";
119567
119836
  return "failed";
119568
119837
  }
119569
- const make$36 = Effect.gen(function* () {
119838
+ const make$38 = Effect.gen(function* () {
119570
119839
  const cli = yield* GitLabPullRequestCli;
119571
119840
  const fail = (operation) => (error) => new PullRequestProviderError({
119572
119841
  provider: "gitlab",
@@ -119709,13 +119978,13 @@ function fromProviders(providers) {
119709
119978
  * The hosts this build can read change requests from. A host with no entry here still shows up
119710
119979
  * in the provider list as unimplemented, so its projects are explained rather than missing.
119711
119980
  */
119712
- const make$35 = Effect.map(Effect.all([
119713
- make$38,
119714
- make$36,
119981
+ const make$37 = Effect.map(Effect.all([
119715
119982
  make$40,
119716
- make$42
119983
+ make$38,
119984
+ make$42,
119985
+ make$44
119717
119986
  ]), fromProviders);
119718
- const layer$29 = Layer.effect(PullRequestProviderRegistry, make$35).pipe(Layer.provide(layer$31.pipe(Layer.provide(layer$40))), Layer.provide(layer$30.pipe(Layer.provide(layer$38))), Layer.provide(layer$32.pipe(Layer.provide(layer$42))), Layer.provide(layer$33.pipe(Layer.provide(layer$44))));
119987
+ const layer$29 = Layer.effect(PullRequestProviderRegistry, make$37).pipe(Layer.provide(layer$31.pipe(Layer.provide(layer$40))), Layer.provide(layer$30.pipe(Layer.provide(layer$38))), Layer.provide(layer$32.pipe(Layer.provide(layer$42))), Layer.provide(layer$33.pipe(Layer.provide(layer$44))));
119719
119988
  //#endregion
119720
119989
  //#region src/pullRequest/PullRequestService.ts
119721
119990
  /**
@@ -119903,7 +120172,7 @@ function repositoryIdentityOf(project) {
119903
120172
  if (identity.displayName) return identity.displayName;
119904
120173
  return identity.owner && identity.name ? `${identity.owner}/${identity.name}` : null;
119905
120174
  }
119906
- const make$34 = Effect.gen(function* () {
120175
+ const make$36 = Effect.gen(function* () {
119907
120176
  const registry = yield* PullRequestProviderRegistry;
119908
120177
  const projections = yield* ProjectionSnapshotQuery;
119909
120178
  const sourceControlProviders = yield* SourceControlProviderRegistry;
@@ -120901,7 +121170,7 @@ const make$34 = Effect.gen(function* () {
120901
121170
  invalidate
120902
121171
  });
120903
121172
  });
120904
- const layer$28 = Layer.effect(PullRequestService, make$34);
121173
+ const layer$28 = Layer.effect(PullRequestService, make$36);
120905
121174
  //#endregion
120906
121175
  //#region src/orchestration/ThreadWorkspaceLifecycle.ts
120907
121176
  var ThreadWorkspaceLifecycleError = class extends Data.TaggedError("ThreadWorkspaceLifecycleError") {};
@@ -120945,7 +121214,7 @@ const mapLifecycleError = Effect.mapError((cause) => cause instanceof ThreadWork
120945
121214
  detail: "Thread workspace lifecycle operation failed.",
120946
121215
  cause
120947
121216
  }));
120948
- const make$33 = Effect.gen(function* () {
121217
+ const make$35 = Effect.gen(function* () {
120949
121218
  const snapshots = yield* ProjectionSnapshotQuery;
120950
121219
  const engine = yield* OrchestrationEngineService;
120951
121220
  const gitWorkflow = yield* GitWorkflowService;
@@ -121199,7 +121468,7 @@ const make$33 = Effect.gen(function* () {
121199
121468
  record
121200
121469
  };
121201
121470
  });
121202
- const layer$27 = Layer.effect(ThreadWorkspaceLifecycleService, make$33);
121471
+ const layer$27 = Layer.effect(ThreadWorkspaceLifecycleService, make$35);
121203
121472
  //#endregion
121204
121473
  //#region src/textGeneration/BtwRequestCoordinator.ts
121205
121474
  const MAX_PENDING_BTW_CANCELLATIONS = 256;
@@ -123472,7 +123741,7 @@ function makeUpdateState(input) {
123472
123741
  output: input.output ?? null
123473
123742
  };
123474
123743
  }
123475
- const make$32 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
123744
+ const make$34 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
123476
123745
  const providerRegistry = yield* ProviderRegistry;
123477
123746
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
123478
123747
  const httpClient = yield* HttpClient.HttpClient;
@@ -123587,7 +123856,7 @@ const make$32 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
123587
123856
  });
123588
123857
  return ProviderMaintenanceRunner.of({ updateProvider });
123589
123858
  });
123590
- const layer$26 = Layer.effect(ProviderMaintenanceRunner, make$32());
123859
+ const layer$26 = Layer.effect(ProviderMaintenanceRunner, make$34());
123591
123860
  //#endregion
123592
123861
  //#region src/usage/usageTranscripts.ts
123593
123862
  const EMPTY_TOTALS = {
@@ -124413,7 +124682,7 @@ Layer.succeed(UsageService, UsageService.of({
124413
124682
  scanDurationMs: 0
124414
124683
  })
124415
124684
  }));
124416
- const make$31 = Effect.gen(function* () {
124685
+ const make$33 = Effect.gen(function* () {
124417
124686
  const fileSystem = yield* FileSystem.FileSystem;
124418
124687
  const path = yield* Path$1.Path;
124419
124688
  const config = yield* ServerConfig$1;
@@ -124696,7 +124965,7 @@ const make$31 = Effect.gen(function* () {
124696
124965
  readSessionRecords: (input) => scanLock.withPermits(1)(readSessionRecords(input))
124697
124966
  };
124698
124967
  });
124699
- const layer$25 = Layer.effect(UsageService, make$31);
124968
+ const layer$25 = Layer.effect(UsageService, make$33);
124700
124969
  //#endregion
124701
124970
  //#region src/feed/FeedStore.ts
124702
124971
  const storageFailure = (message) => new FeedError({
@@ -125060,7 +125329,7 @@ const jsonRequest = Effect.fn("FeedService.jsonRequest")(function* (url, token,
125060
125329
  catch: () => fail("hub_unavailable", "Hub returned invalid JSON.")
125061
125330
  });
125062
125331
  });
125063
- const make$30 = Effect.gen(function* () {
125332
+ const make$32 = Effect.gen(function* () {
125064
125333
  const hubLink = yield* HubLink;
125065
125334
  const providers = yield* ProviderInstanceRegistry;
125066
125335
  const config = yield* ServerConfig$1;
@@ -125298,7 +125567,7 @@ var FeedService = class extends Context.Reference("@p4code/cli/feed/FeedService"
125298
125567
  markRead: unavailable,
125299
125568
  cleanup: unavailable
125300
125569
  }) }) {};
125301
- const layer$24 = Layer.effect(FeedService, make$30);
125570
+ const layer$24 = Layer.effect(FeedService, make$32);
125302
125571
  //#endregion
125303
125572
  //#region src/storageCleanup.ts
125304
125573
  const DAY_MS$1 = 864e5;
@@ -125315,7 +125584,7 @@ function isExpired(modifiedAtMs, days, nowMs) {
125315
125584
  return modifiedAtMs < nowMs - days * DAY_MS$1;
125316
125585
  }
125317
125586
  var StorageCleanup = class extends Context.Service()("@p4code/cli/storageCleanup") {};
125318
- const make$29 = Effect.gen(function* () {
125587
+ const make$31 = Effect.gen(function* () {
125319
125588
  const fs = yield* FileSystem.FileSystem;
125320
125589
  const path = yield* Path$1.Path;
125321
125590
  const config = yield* ServerConfig$1;
@@ -125404,7 +125673,7 @@ const make$29 = Effect.gen(function* () {
125404
125673
  })
125405
125674
  };
125406
125675
  });
125407
- const layer$23 = Layer.effect(StorageCleanup, make$29);
125676
+ const layer$23 = Layer.effect(StorageCleanup, make$31);
125408
125677
  //#endregion
125409
125678
  //#region src/worktreeCleanupRules.ts
125410
125679
  /**
@@ -125504,7 +125773,7 @@ function planWorktreeCleanup(facts, settings, nowMs) {
125504
125773
  * @module worktreeCleanup
125505
125774
  */
125506
125775
  var WorktreeCleanup = class extends Context.Service()("@p4code/cli/worktreeCleanup") {};
125507
- const make$28 = Effect.gen(function* () {
125776
+ const make$30 = Effect.gen(function* () {
125508
125777
  const threads = yield* ProjectionThreadRepository;
125509
125778
  const projects = yield* ProjectionProjectRepository;
125510
125779
  const sessions = yield* ProviderSessionDirectory;
@@ -125599,7 +125868,7 @@ const make$28 = Effect.gen(function* () {
125599
125868
  })
125600
125869
  };
125601
125870
  });
125602
- const layer$22 = Layer.effect(WorktreeCleanup, make$28);
125871
+ const layer$22 = Layer.effect(WorktreeCleanup, make$30);
125603
125872
  const SKILL_MANIFEST_FILENAME = "SKILL.md";
125604
125873
  /**
125605
125874
  * Split a catalogue id (`owner/repo/skill-name`) into its parts.
@@ -125745,7 +126014,7 @@ const emptyFetch = (id, unavailable) => ({
125745
126014
  skipped: [],
125746
126015
  unavailable
125747
126016
  });
125748
- const make$27 = Effect.gen(function* () {
126017
+ const make$29 = Effect.gen(function* () {
125749
126018
  const http = yield* HttpClient.HttpClient;
125750
126019
  const request = Effect.fn("SkillRegistry.request")(function* (url) {
125751
126020
  return yield* http.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.setHeader("accept", "application/json"), HttpClientRequest.setHeader("user-agent", "p4code"))).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$1));
@@ -125816,7 +126085,7 @@ const make$27 = Effect.gen(function* () {
125816
126085
  fetch
125817
126086
  };
125818
126087
  });
125819
- const layer$21 = Layer.effect(SkillRegistry, make$27);
126088
+ const layer$21 = Layer.effect(SkillRegistry, make$29);
125820
126089
  //#endregion
125821
126090
  //#region src/orchestration/SupervisorSecrets.ts
125822
126091
  /**
@@ -125892,6 +126161,9 @@ const makeSupervisorSecrets = Effect.gen(function* () {
125892
126161
  });
125893
126162
  const SupervisorSecretsLive = Layer.effect(SupervisorSecrets, makeSupervisorSecrets);
125894
126163
  //#endregion
126164
+ //#region src/orchestration/Services/JevWorkspaceAdvisor.ts
126165
+ var JevWorkspaceAdvisor = class extends Context.Service()("@p4code/cli/orchestration/Services/JevWorkspaceAdvisor") {};
126166
+ //#endregion
125895
126167
  //#region src/mcp/PreviewAutomationBroker.ts
125896
126168
  var PreviewAutomationBroker = class extends Context.Service()("@p4code/cli/mcp/PreviewAutomationBroker") {};
125897
126169
  const removeConnectionFromState = (current, clientId, queue) => {
@@ -126008,7 +126280,7 @@ const classifyResponseError = (context, error) => {
126008
126280
  });
126009
126281
  }
126010
126282
  };
126011
- const make$26 = Effect.gen(function* PreviewAutomationBrokerMake() {
126283
+ const make$28 = Effect.gen(function* PreviewAutomationBrokerMake() {
126012
126284
  const crypto = yield* Crypto.Crypto;
126013
126285
  const state = yield* SynchronizedRef.make({
126014
126286
  clients: /* @__PURE__ */ new Map(),
@@ -126242,7 +126514,7 @@ const make$26 = Effect.gen(function* PreviewAutomationBrokerMake() {
126242
126514
  invoke
126243
126515
  });
126244
126516
  }).pipe(Effect.withSpan("PreviewAutomationBroker.make"));
126245
- const layer$20 = Layer.effect(PreviewAutomationBroker, make$26);
126517
+ const layer$20 = Layer.effect(PreviewAutomationBroker, make$28);
126246
126518
  //#endregion
126247
126519
  //#region src/preview/Manager.ts
126248
126520
  /**
@@ -126306,7 +126578,7 @@ const buildIdleSnapshot = (input) => ({
126306
126578
  viewport: FILL_PREVIEW_VIEWPORT,
126307
126579
  updatedAt: input.updatedAt
126308
126580
  });
126309
- const make$25 = Effect.gen(function* PreviewManagerMake() {
126581
+ const make$27 = Effect.gen(function* PreviewManagerMake() {
126310
126582
  const serverEpoch = NodeCrypto.randomUUID();
126311
126583
  const stateRef = yield* SynchronizedRef.make(initialState);
126312
126584
  const eventsPubSub = yield* PubSub.unbounded();
@@ -126537,7 +126809,7 @@ const make$25 = Effect.gen(function* PreviewManagerMake() {
126537
126809
  subscribeEvents: PubSub.subscribe(eventsPubSub)
126538
126810
  });
126539
126811
  }).pipe(Effect.withSpan("PreviewManager.make"));
126540
- const layer$19 = Layer.effect(PreviewManager, make$25);
126812
+ const layer$19 = Layer.effect(PreviewManager, make$27);
126541
126813
  //#endregion
126542
126814
  //#region src/workspace/WorkspaceSearchIndex.ts
126543
126815
  const WORKSPACE_INDEX_MAX_ENTRIES = 25e3;
@@ -126674,7 +126946,7 @@ const waitForScan = (cwd, finder, onFailure) => Effect.try({
126674
126946
  timeout: WORKSPACE_INDEX_SCAN_TIMEOUT
126675
126947
  })
126676
126948
  }), Effect.withSpan("WorkspaceSearchIndex.waitForScan"));
126677
- const make$24 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd, contentSearch = false) {
126949
+ const make$26 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd, contentSearch = false) {
126678
126950
  const finder = yield* Effect.acquireRelease(createFinder(cwd, contentSearch), (finder) => Effect.try({
126679
126951
  try: () => finder.destroy(),
126680
126952
  catch: (cause) => new WorkspaceSearchIndexDestroyFailed({
@@ -126814,14 +127086,14 @@ const make$24 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd, contentSe
126814
127086
  * workspace root. WorkspaceSearchIndexMap owns memoization and idle cleanup;
126815
127087
  * using a default cwd here would mix resources from different workspaces.
126816
127088
  */
126817
- const layer$18 = (cwd) => Layer.effect(WorkspaceSearchIndex, make$24(cwd));
127089
+ const layer$18 = (cwd) => Layer.effect(WorkspaceSearchIndex, make$26(cwd));
126818
127090
  var WorkspaceSearchIndexMap = class extends LayerMap.Service()("@p4code/cli/workspace/WorkspaceSearchIndexMap", {
126819
127091
  lookup: layer$18,
126820
127092
  idleTimeToLive: WORKSPACE_INDEX_IDLE_TTL
126821
127093
  }) {};
126822
127094
  /** Content indexing is allocated only when workspace content search is used. */
126823
127095
  var WorkspaceContentSearchIndexMap = class extends LayerMap.Service()("@p4code/cli/workspace/WorkspaceContentSearchIndexMap", {
126824
- lookup: (cwd) => Layer.effect(WorkspaceSearchIndex, make$24(cwd, true)),
127096
+ lookup: (cwd) => Layer.effect(WorkspaceSearchIndex, make$26(cwd, true)),
126825
127097
  idleTimeToLive: WORKSPACE_INDEX_IDLE_TTL
126826
127098
  }) {};
126827
127099
  //#endregion
@@ -126883,7 +127155,7 @@ const resolveBrowseTarget = Effect.fn("WorkspaceEntries.resolveBrowseTarget")(fu
126883
127155
  if (!input.cwd) return yield* new WorkspaceEntriesCurrentProjectRequiredError({ partialPath: input.partialPath });
126884
127156
  return path.resolve(expandHomePath$1(input.cwd, path), input.partialPath);
126885
127157
  });
126886
- const make$23 = Effect.gen(function* () {
127158
+ const make$25 = Effect.gen(function* () {
126887
127159
  const path = yield* Path$1.Path;
126888
127160
  const workspacePaths = yield* WorkspacePaths;
126889
127161
  const contentSearchIndexes = yield* WorkspaceContentSearchIndexMap;
@@ -126965,7 +127237,7 @@ const make$23 = Effect.gen(function* () {
126965
127237
  searchContents
126966
127238
  });
126967
127239
  });
126968
- const layer$17 = Layer.effect(WorkspaceEntries, make$23).pipe(Layer.provide(WorkspaceSearchIndexMap.layer), Layer.provide(WorkspaceContentSearchIndexMap.layer));
127240
+ const layer$17 = Layer.effect(WorkspaceEntries, make$25).pipe(Layer.provide(WorkspaceSearchIndexMap.layer), Layer.provide(WorkspaceContentSearchIndexMap.layer));
126969
127241
  //#endregion
126970
127242
  //#region src/workspace/WorkspaceFileSystem.ts
126971
127243
  /**
@@ -127024,7 +127296,7 @@ Schema$1.Union([
127024
127296
  ]);
127025
127297
  /** Service tag for workspace file operations. */
127026
127298
  var WorkspaceFileSystem = class extends Context.Service()("@p4code/cli/workspace/WorkspaceFileSystem") {};
127027
- const make$22 = Effect.gen(function* () {
127299
+ const make$24 = Effect.gen(function* () {
127028
127300
  const fileSystem = yield* FileSystem.FileSystem;
127029
127301
  const path = yield* Path$1.Path;
127030
127302
  const workspacePaths = yield* WorkspacePaths;
@@ -127168,7 +127440,7 @@ const make$22 = Effect.gen(function* () {
127168
127440
  writeFile
127169
127441
  });
127170
127442
  });
127171
- const layer$16 = Layer.effect(WorkspaceFileSystem, make$22);
127443
+ const layer$16 = Layer.effect(WorkspaceFileSystem, make$24);
127172
127444
  //#endregion
127173
127445
  //#region src/vcs/VcsStatusBroadcaster.ts
127174
127446
  const DEFAULT_VCS_STATUS_REFRESH_INTERVAL = Duration.seconds(30);
@@ -127250,7 +127522,7 @@ const autoPullPolicyLayer = Layer.effect(VcsAutoPullPolicy, Effect.gen(function*
127250
127522
  return resolveProjectAutoPull(yield* settingsService.getSettings, project.value.id);
127251
127523
  }, Effect.orElseSucceed(() => false)) };
127252
127524
  }));
127253
- const make$21 = Effect.gen(function* () {
127525
+ const make$23 = Effect.gen(function* () {
127254
127526
  const autoPullPolicy = yield* VcsAutoPullPolicy;
127255
127527
  const workflow = yield* GitWorkflowService;
127256
127528
  const fs = yield* FileSystem.FileSystem;
@@ -127509,7 +127781,7 @@ const make$21 = Effect.gen(function* () {
127509
127781
  streamStatus
127510
127782
  });
127511
127783
  });
127512
- const layer$15 = Layer.effect(VcsStatusBroadcaster, make$21);
127784
+ const layer$15 = Layer.effect(VcsStatusBroadcaster, make$23);
127513
127785
  //#endregion
127514
127786
  //#region src/vcs/VcsProvisioningService.ts
127515
127787
  var VcsProvisioningService = class extends Context.Service()("@p4code/cli/vcs/VcsProvisioningService") {};
@@ -127522,7 +127794,7 @@ function resolveRequestedKind(kind) {
127522
127794
  }));
127523
127795
  return Effect.succeed(kind);
127524
127796
  }
127525
- const make$20 = Effect.gen(function* () {
127797
+ const make$22 = Effect.gen(function* () {
127526
127798
  const registry = yield* VcsDriverRegistry;
127527
127799
  const initRepository = Effect.fn("VcsProvisioningService.initRepository")(function* (input) {
127528
127800
  const kind = yield* resolveRequestedKind(input.kind);
@@ -127530,11 +127802,11 @@ const make$20 = Effect.gen(function* () {
127530
127802
  });
127531
127803
  return VcsProvisioningService.of({ initRepository });
127532
127804
  });
127533
- const layer$14 = Layer.effect(VcsProvisioningService, make$20);
127805
+ const layer$14 = Layer.effect(VcsProvisioningService, make$22);
127534
127806
  //#endregion
127535
127807
  //#region src/review/ReviewService.ts
127536
127808
  var ReviewService = class extends Context.Service()("@p4code/cli/review/ReviewService") {};
127537
- const make$19 = Effect.gen(function* () {
127809
+ const make$21 = Effect.gen(function* () {
127538
127810
  const config = yield* ServerConfig$1;
127539
127811
  const fileSystem = yield* FileSystem.FileSystem;
127540
127812
  const path = yield* Path$1.Path;
@@ -127590,7 +127862,7 @@ const make$19 = Effect.gen(function* () {
127590
127862
  });
127591
127863
  return ReviewService.of({ getDiffPreview });
127592
127864
  });
127593
- const layer$13 = Layer.effect(ReviewService, make$19);
127865
+ const layer$13 = Layer.effect(ReviewService, make$21);
127594
127866
  //#endregion
127595
127867
  //#region src/diagnostics/ProcessDiagnostics.ts
127596
127868
  const PROCESS_QUERY_TIMEOUT_MS = 1e3;
@@ -127885,7 +128157,7 @@ function assertDescendantPid(pid) {
127885
128157
  }));
127886
128158
  }));
127887
128159
  }
127888
- const make$18 = Effect.gen(function* () {
128160
+ const make$20 = Effect.gen(function* () {
127889
128161
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
127890
128162
  const read = Effect.gen(function* () {
127891
128163
  const readAt = yield* DateTime.now;
@@ -127929,7 +128201,7 @@ const make$18 = Effect.gen(function* () {
127929
128201
  signal
127930
128202
  });
127931
128203
  });
127932
- const layer$12 = Layer.effect(ProcessDiagnostics, make$18);
128204
+ const layer$12 = Layer.effect(ProcessDiagnostics, make$20);
127933
128205
  //#endregion
127934
128206
  //#region src/diagnostics/ProcessResourceMonitor.ts
127935
128207
  const SAMPLE_INTERVAL_MS = 5e3;
@@ -128080,7 +128352,7 @@ function aggregateProcessResourceHistory(input) {
128080
128352
  }) : Option.none()
128081
128353
  };
128082
128354
  }
128083
- const make$17 = Effect.gen(function* () {
128355
+ const make$19 = Effect.gen(function* () {
128084
128356
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
128085
128357
  const state = yield* Ref.make({
128086
128358
  samples: [],
@@ -128129,7 +128401,7 @@ const make$17 = Effect.gen(function* () {
128129
128401
  });
128130
128402
  return ProcessResourceMonitor.of({ readHistory });
128131
128403
  });
128132
- const layer$11 = Layer.effect(ProcessResourceMonitor, make$17);
128404
+ const layer$11 = Layer.effect(ProcessResourceMonitor, make$19);
128133
128405
  //#endregion
128134
128406
  //#region src/diagnostics/TraceDiagnostics.ts
128135
128407
  var TraceFileReadError = class extends Schema$1.TaggedErrorClass()("TraceFileReadError", {
@@ -128377,7 +128649,7 @@ function readTraceFile(fileSystem, path) {
128377
128649
  cause
128378
128650
  })) }));
128379
128651
  }
128380
- const make$16 = Effect.gen(function* () {
128652
+ const make$18 = Effect.gen(function* () {
128381
128653
  const fileSystem = yield* FileSystem.FileSystem;
128382
128654
  const read = Effect.fn("TraceDiagnostics.read")(function* (options) {
128383
128655
  const readAt = options.readAt ?? (yield* DateTime.now);
@@ -128421,7 +128693,7 @@ const make$16 = Effect.gen(function* () {
128421
128693
  });
128422
128694
  return TraceDiagnostics.of({ read });
128423
128695
  });
128424
- const layer$10 = Layer.effect(TraceDiagnostics, make$16);
128696
+ const layer$10 = Layer.effect(TraceDiagnostics, make$18);
128425
128697
  function readTraceDiagnostics(options) {
128426
128698
  return Effect.gen(function* () {
128427
128699
  return yield* (yield* TraceDiagnostics).read(options);
@@ -128456,7 +128728,7 @@ function snapshotFromDetail(link, detail, syncedAt) {
128456
128728
  mergeability: detail.mergeability
128457
128729
  };
128458
128730
  }
128459
- const make$15 = Effect.gen(function* () {
128731
+ const make$17 = Effect.gen(function* () {
128460
128732
  const engine = yield* OrchestrationEngineService;
128461
128733
  const projections = yield* ProjectionSnapshotQuery;
128462
128734
  const pullRequests = yield* PullRequestService;
@@ -128550,7 +128822,7 @@ const make$15 = Effect.gen(function* () {
128550
128822
  };
128551
128823
  });
128552
128824
  var PullRequestSyncReactor = class extends Context.Service()("@p4code/cli/orchestration/Layers/PullRequestSyncReactor") {};
128553
- const PullRequestSyncReactorLive = Layer.effect(PullRequestSyncReactor, make$15);
128825
+ const PullRequestSyncReactorLive = Layer.effect(PullRequestSyncReactor, make$17);
128554
128826
  //#endregion
128555
128827
  //#region src/sourceControl/SourceControlDiscovery.ts
128556
128828
  const VCS_PROBES = [{
@@ -128569,7 +128841,7 @@ const VCS_PROBES = [{
128569
128841
  installHint: "Install Jujutsu with `brew install jj` or from https://github.com/jj-vcs/jj."
128570
128842
  }];
128571
128843
  var SourceControlDiscovery = class extends Context.Service()("@p4code/cli/sourceControl/SourceControlDiscovery") {};
128572
- const make$14 = Effect.gen(function* () {
128844
+ const make$16 = Effect.gen(function* () {
128573
128845
  const config = yield* ServerConfig$1;
128574
128846
  const process = yield* VcsProcess;
128575
128847
  const sourceControlProviders = yield* SourceControlProviderRegistry;
@@ -128618,7 +128890,7 @@ const make$14 = Effect.gen(function* () {
128618
128890
  sourceControlProviders: sourceControlProviders.discover
128619
128891
  }) });
128620
128892
  });
128621
- const layer$9 = Layer.effect(SourceControlDiscovery, make$14);
128893
+ const layer$9 = Layer.effect(SourceControlDiscovery, make$16);
128622
128894
  //#endregion
128623
128895
  //#region src/sourceControl/SourceControlRepositoryService.ts
128624
128896
  const isSourceControlRepositoryError = Schema$1.is(SourceControlRepositoryError);
@@ -128651,7 +128923,7 @@ function expandHomePath(input, path) {
128651
128923
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
128652
128924
  return input;
128653
128925
  }
128654
- const make$13 = Effect.gen(function* () {
128926
+ const make$15 = Effect.gen(function* () {
128655
128927
  const config = yield* ServerConfig$1;
128656
128928
  const fileSystem = yield* FileSystem.FileSystem;
128657
128929
  const git = yield* GitVcsDriver;
@@ -128790,7 +129062,7 @@ const make$13 = Effect.gen(function* () {
128790
129062
  publishRepository: (input) => publishRepository(input).pipe(mapRepositoryError("publishRepository", input.provider))
128791
129063
  });
128792
129064
  });
128793
- const layer$8 = Layer.effect(SourceControlRepositoryService, make$13);
129065
+ const layer$8 = Layer.effect(SourceControlRepositoryService, make$15);
128794
129066
  //#endregion
128795
129067
  //#region src/ws.ts
128796
129068
  /** Matches `p4c hub token add`, so a token minted here and one minted there are the same thing. */
@@ -129163,6 +129435,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
129163
129435
  const mcpOAuth = yield* McpOAuth;
129164
129436
  const linearClient = yield* LinearClient;
129165
129437
  const supervisorSecrets = yield* SupervisorSecrets;
129438
+ const jevWorkspaceAdvisor = yield* JevWorkspaceAdvisor;
129166
129439
  /** Status with the key masked. The unmasked value never leaves the store. */
129167
129440
  const supervisorSecretStatuses = supervisorSecrets.status.pipe(Effect.flatMap((entries) => Effect.forEach(entries, (entry) => supervisorSecrets.read(entry.service).pipe(Effect.map((credential) => ({
129168
129441
  ...entry,
@@ -129501,7 +129774,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
129501
129774
  }),
129502
129775
  hasWorkingTreeChanges: localStatus.hasWorkingTreeChanges,
129503
129776
  modelSelection: command.modelSelection ?? currentThread?.modelSelection
129504
- }, textGeneration);
129777
+ }, textGeneration, jevWorkspaceAdvisor);
129505
129778
  if (decision.mode === "local" && localStatus.isRepo && isAutoCheckoutBusy({
129506
129779
  snapshot: yield* projectionSnapshotQuery.getCommandReadModel(),
129507
129780
  threadId: command.threadId,
@@ -129525,6 +129798,24 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
129525
129798
  },
129526
129799
  createdAt: command.createdAt
129527
129800
  });
129801
+ if (decision.source === "jev") yield* orchestrationEngine.dispatch({
129802
+ type: "thread.activity.append",
129803
+ commandId: yield* serverCommandId("jev-consulted-workspace"),
129804
+ threadId: command.threadId,
129805
+ activity: {
129806
+ id: yield* serverEventId,
129807
+ tone: "info",
129808
+ kind: JEV_CONSULTED_ACTIVITY_KIND,
129809
+ turnId: null,
129810
+ summary: "Consulted with Jev",
129811
+ payload: {
129812
+ feature: "auto-workspace",
129813
+ summary: decision.mode === "worktree" ? "Chose a new worktree" : "Chose the current checkout"
129814
+ },
129815
+ createdAt: command.createdAt
129816
+ },
129817
+ createdAt: command.createdAt
129818
+ });
129528
129819
  if (decision.mode === "local") {
129529
129820
  yield* orchestrationEngine.dispatch({
129530
129821
  type: "thread.meta.update",
@@ -130369,7 +130660,7 @@ function toPersistenceSqlOrDecodeError(sqlOperation, decodeOperation, correlatio
130369
130660
  cause
130370
130661
  });
130371
130662
  }
130372
- const make$12 = Effect.gen(function* () {
130663
+ const make$14 = Effect.gen(function* () {
130373
130664
  const sql = yield* SqlClient.SqlClient;
130374
130665
  const upsertRuntimeRow = SqlSchema.void({
130375
130666
  Request: ProviderSessionRuntimeDbRowSchema,
@@ -130472,7 +130763,7 @@ const make$12 = Effect.gen(function* () {
130472
130763
  deleteByThreadId
130473
130764
  };
130474
130765
  });
130475
- const layer$7 = Layer.effect(ProviderSessionRuntimeRepository, make$12);
130766
+ const layer$7 = Layer.effect(ProviderSessionRuntimeRepository, make$14);
130476
130767
  //#endregion
130477
130768
  //#region src/provider/Layers/ProviderSessionDirectory.ts
130478
130769
  const decodeProviderDriverKindValue = Schema$1.decodeUnknownEffect(ProviderDriverKind);
@@ -130660,14 +130951,15 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130660
130951
  const httpServer = yield* HttpServer.HttpServer;
130661
130952
  const state = yield* SynchronizedRef.make({
130662
130953
  records: /* @__PURE__ */ new Map(),
130663
- spawnedThreads: /* @__PURE__ */ new Map()
130954
+ spawnedThreads: /* @__PURE__ */ new Map(),
130955
+ fusionRoles: /* @__PURE__ */ new Map()
130664
130956
  });
130665
130957
  const currentTimeMillis = options.now ? Effect.sync(options.now) : Clock.currentTimeMillis;
130666
130958
  const livenessWindowMs = options.livenessWindowMs ?? DEFAULT_LIVENESS_WINDOW_MS;
130667
130959
  const endpoint = httpServer.address._tag === "TcpAddress" ? `http://${getHttpMcpEndpointHost(httpServer.address.hostname)}:${httpServer.address.port}/mcp` : "http://127.0.0.1/mcp";
130668
130960
  const hashToken = (token) => crypto.digest("SHA-256", new TextEncoder().encode(token)).pipe(Effect.map(bytesToHex), Effect.orDie);
130669
130961
  const revokeWhere = Effect.fn("McpSessionRegistry.revokeWhere")(function* (predicate) {
130670
- const removed = yield* SynchronizedRef.modify(state, ({ records, spawnedThreads }) => {
130962
+ const removed = yield* SynchronizedRef.modify(state, ({ records, spawnedThreads, fusionRoles }) => {
130671
130963
  const next = new Map(records);
130672
130964
  const scopes = [];
130673
130965
  for (const [hash, record] of records) {
@@ -130678,7 +130970,8 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130678
130970
  }
130679
130971
  return [scopes, {
130680
130972
  records: next,
130681
- spawnedThreads
130973
+ spawnedThreads,
130974
+ fusionRoles
130682
130975
  }];
130683
130976
  });
130684
130977
  yield* Effect.forEach(removed, (scope) => scope.authorizationLifetime?.revocation ? Deferred.succeed(scope.authorizationLifetime.revocation, void 0) : Effect.void, { discard: true });
@@ -130706,6 +130999,8 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130706
130999
  if (watchThreadIds.size > 0) capabilities.add("watch");
130707
131000
  if (adviseThreadIds.size > 0) capabilities.add("advise");
130708
131001
  const threadId = ThreadId.make(request.threadId);
131002
+ const rememberedRole = yield* SynchronizedRef.get(state).pipe(Effect.map((current) => current.fusionRoles.get(threadId)));
131003
+ const fusionRole = rememberedRole === void 0 ? request.fusionRole : rememberedRole ?? void 0;
130709
131004
  const scopeWith = (mayCreateThreads, mayCreateFusionPairs, revocation = mainRevocation) => ({
130710
131005
  authorizationLifetime: {
130711
131006
  revoked: false,
@@ -130719,11 +131014,12 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130719
131014
  capabilities,
130720
131015
  ...watchThreadIds.size > 0 ? { watchThreadIds } : {},
130721
131016
  ...adviseThreadIds.size > 0 ? { adviseThreadIds } : {},
131017
+ ...fusionRole === void 0 ? {} : { fusionRole },
130722
131018
  mayCreateThreads,
130723
131019
  mayCreateFusionPairs,
130724
131020
  issuedAt
130725
131021
  });
130726
- yield* SynchronizedRef.update(state, ({ records, spawnedThreads }) => {
131022
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreads, fusionRoles }) => {
130727
131023
  const next = new Map(records);
130728
131024
  next.set(tokenHash, {
130729
131025
  tokenHash,
@@ -130743,7 +131039,8 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130743
131039
  });
130744
131040
  return {
130745
131041
  records: next,
130746
- spawnedThreads
131042
+ spawnedThreads,
131043
+ fusionRoles
130747
131044
  };
130748
131045
  });
130749
131046
  return { config: {
@@ -130765,12 +131062,13 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130765
131062
  const tokenHash = yield* hashToken(rawToken);
130766
131063
  const timestamp = yield* currentTimeMillis;
130767
131064
  yield* pruneDead(timestamp);
130768
- return yield* SynchronizedRef.modify(state, ({ records, spawnedThreads }) => {
131065
+ return yield* SynchronizedRef.modify(state, ({ records, spawnedThreads, fusionRoles }) => {
130769
131066
  const current = records;
130770
131067
  const record = current.get(tokenHash);
130771
131068
  if (!record) return [void 0, {
130772
131069
  records: current,
130773
- spawnedThreads
131070
+ spawnedThreads,
131071
+ fusionRoles
130774
131072
  }];
130775
131073
  const next = new Map(current);
130776
131074
  next.set(tokenHash, {
@@ -130784,14 +131082,15 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130784
131082
  watchThreadIds: /* @__PURE__ */ new Set([...record.scope.watchThreadIds ?? [], ...record.controlThreadIds])
130785
131083
  }, {
130786
131084
  records: next,
130787
- spawnedThreads
131085
+ spawnedThreads,
131086
+ fusionRoles
130788
131087
  }];
130789
131088
  });
130790
131089
  });
130791
131090
  const touch = Effect.fn("McpSessionRegistry.touch")(function* (threadId) {
130792
131091
  const timestamp = yield* currentTimeMillis;
130793
131092
  yield* pruneDead(timestamp);
130794
- yield* SynchronizedRef.update(state, ({ records, spawnedThreads }) => {
131093
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreads, fusionRoles }) => {
130795
131094
  const current = records;
130796
131095
  const next = new Map(current);
130797
131096
  for (const [tokenHash, record] of current) if (record.scope.threadId === threadId) next.set(tokenHash, {
@@ -130800,12 +131099,13 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130800
131099
  });
130801
131100
  return {
130802
131101
  records: next,
130803
- spawnedThreads
131102
+ spawnedThreads,
131103
+ fusionRoles
130804
131104
  };
130805
131105
  });
130806
131106
  });
130807
131107
  const grantWatchThread = Effect.fn("McpSessionRegistry.grantWatchThread")(function* ({ watcherThreadId, watchedThreadId }) {
130808
- yield* SynchronizedRef.update(state, ({ records, spawnedThreads }) => {
131108
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreads, fusionRoles }) => {
130809
131109
  const next = new Map(records);
130810
131110
  for (const [tokenHash, record] of records) {
130811
131111
  if (record.scope.threadId !== watcherThreadId) continue;
@@ -130820,12 +131120,13 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130820
131120
  }
130821
131121
  return {
130822
131122
  records: next,
130823
- spawnedThreads
131123
+ spawnedThreads,
131124
+ fusionRoles
130824
131125
  };
130825
131126
  });
130826
131127
  });
130827
131128
  const revokeWatchThread = Effect.fn("McpSessionRegistry.revokeWatchThread")(function* ({ watcherThreadId, watchedThreadId }) {
130828
- yield* SynchronizedRef.update(state, ({ records, spawnedThreads }) => {
131129
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreads, fusionRoles }) => {
130829
131130
  const next = new Map(records);
130830
131131
  for (const [tokenHash, record] of records) {
130831
131132
  if (record.scope.threadId !== watcherThreadId) continue;
@@ -130848,12 +131149,13 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130848
131149
  }
130849
131150
  return {
130850
131151
  records: next,
130851
- spawnedThreads
131152
+ spawnedThreads,
131153
+ fusionRoles
130852
131154
  };
130853
131155
  });
130854
131156
  });
130855
131157
  const grantAdviseThread = Effect.fn("McpSessionRegistry.grantAdviseThread")(function* ({ watcherThreadId, advisedThreadId }) {
130856
- yield* SynchronizedRef.update(state, ({ records, spawnedThreads }) => {
131158
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreads, fusionRoles }) => {
130857
131159
  const next = new Map(records);
130858
131160
  for (const [tokenHash, record] of records) {
130859
131161
  if (record.scope.threadId !== watcherThreadId) continue;
@@ -130868,12 +131170,13 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130868
131170
  }
130869
131171
  return {
130870
131172
  records: next,
130871
- spawnedThreads
131173
+ spawnedThreads,
131174
+ fusionRoles
130872
131175
  };
130873
131176
  });
130874
131177
  });
130875
131178
  const revokeAdviseThread = Effect.fn("McpSessionRegistry.revokeAdviseThread")(function* ({ watcherThreadId, advisedThreadId }) {
130876
- yield* SynchronizedRef.update(state, ({ records, spawnedThreads }) => {
131179
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreads, fusionRoles }) => {
130877
131180
  const next = new Map(records);
130878
131181
  for (const [tokenHash, record] of records) {
130879
131182
  if (record.scope.threadId !== watcherThreadId) continue;
@@ -130896,12 +131199,47 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130896
131199
  }
130897
131200
  return {
130898
131201
  records: next,
130899
- spawnedThreads
131202
+ spawnedThreads,
131203
+ fusionRoles
130900
131204
  };
130901
131205
  });
130902
131206
  });
131207
+ /**
131208
+ * Rewrites one thread's half on every credential it holds, and remembers it.
131209
+ *
131210
+ * A pair is routinely made over a thread that is already running, so the
131211
+ * credential it is using was minted before the pair existed and carries no
131212
+ * role. Patching the live record is what closes that session's gate without
131213
+ * restarting it; the remembered value is what answers for a session that has
131214
+ * not started yet, or has not reached its `issue` call.
131215
+ */
131216
+ const applyFusionRole = (threadId, role) => SynchronizedRef.update(state, ({ records, spawnedThreads, fusionRoles }) => {
131217
+ const next = new Map(records);
131218
+ for (const [tokenHash, record] of records) {
131219
+ if (record.scope.threadId !== threadId) continue;
131220
+ const { fusionRole: _previous, ...scopeWithoutRole } = record.scope;
131221
+ next.set(tokenHash, {
131222
+ ...record,
131223
+ scope: role === null ? scopeWithoutRole : {
131224
+ ...scopeWithoutRole,
131225
+ fusionRole: role
131226
+ }
131227
+ });
131228
+ }
131229
+ return {
131230
+ records: next,
131231
+ spawnedThreads,
131232
+ fusionRoles: new Map(fusionRoles).set(threadId, role)
131233
+ };
131234
+ });
131235
+ const setFusionRole = Effect.fn("McpSessionRegistry.setFusionRole")(function* (input) {
131236
+ yield* applyFusionRole(input.threadId, input.role);
131237
+ });
131238
+ const clearFusionRole = Effect.fn("McpSessionRegistry.clearFusionRole")(function* (input) {
131239
+ yield* applyFusionRole(input.threadId, null);
131240
+ });
130903
131241
  const recordSpawnedThread = Effect.fn("McpSessionRegistry.recordSpawnedThread")(function* (input) {
130904
- yield* SynchronizedRef.update(state, ({ records, spawnedThreads }) => {
131242
+ yield* SynchronizedRef.update(state, ({ records, spawnedThreads, fusionRoles }) => {
130905
131243
  const next = new Map(records);
130906
131244
  for (const [tokenHash, record] of records) if (record.scope.providerSessionId === input.providerSessionId) next.set(tokenHash, {
130907
131245
  ...record,
@@ -130909,7 +131247,8 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130909
131247
  });
130910
131248
  return {
130911
131249
  records: next,
130912
- spawnedThreads: new Map([...spawnedThreads, [input.threadId, input.mayCreateFusionPairs === true]])
131250
+ spawnedThreads: new Map([...spawnedThreads, [input.threadId, input.mayCreateFusionPairs === true]]),
131251
+ fusionRoles
130913
131252
  };
130914
131253
  });
130915
131254
  });
@@ -130917,6 +131256,8 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130917
131256
  issue,
130918
131257
  resolve,
130919
131258
  touch,
131259
+ setFusionRole,
131260
+ clearFusionRole,
130920
131261
  grantWatchThread,
130921
131262
  revokeWatchThread,
130922
131263
  grantAdviseThread,
@@ -130932,7 +131273,7 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
130932
131273
  });
130933
131274
  });
130934
131275
  let activeMcpSessionRegistry;
130935
- const make$11 = Effect.acquireRelease(Effect.gen(function* () {
131276
+ const make$13 = Effect.acquireRelease(Effect.gen(function* () {
130936
131277
  const broker = yield* Effect.serviceOption(ComputerUseBroker);
130937
131278
  return yield* makeWithOptions(Option.isSome(broker) ? { onRevoke: broker.value.invalidate } : {});
130938
131279
  }).pipe(Effect.tap((registry) => Effect.sync(() => {
@@ -130940,7 +131281,7 @@ const make$11 = Effect.acquireRelease(Effect.gen(function* () {
130940
131281
  }))), (registry) => registry.revokeAll.pipe(Effect.andThen(Effect.sync(() => {
130941
131282
  if (activeMcpSessionRegistry === registry) activeMcpSessionRegistry = void 0;
130942
131283
  }))));
130943
- const layer$6 = Layer.effect(McpSessionRegistry, make$11);
131284
+ const layer$6 = Layer.effect(McpSessionRegistry, make$13);
130944
131285
  const issueActiveMcpCredential = (request) => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeThread(request.threadId).pipe(Effect.andThen(activeMcpSessionRegistry.issue(request))) : Effect.sync(() => void 0);
130945
131286
  /**
130946
131287
  * Refreshes the liveness of a thread's MCP credential. Called on every provider
@@ -130948,6 +131289,8 @@ const issueActiveMcpCredential = (request) => activeMcpSessionRegistry ? activeM
130948
131289
  */
130949
131290
  const touchActiveMcpThread = (threadId) => activeMcpSessionRegistry ? activeMcpSessionRegistry.touch(threadId) : Effect.void;
130950
131291
  const grantActiveMcpWatchThread = (input) => activeMcpSessionRegistry ? activeMcpSessionRegistry.grantWatchThread(input) : Effect.void;
131292
+ const setActiveMcpFusionRole = (input) => activeMcpSessionRegistry ? activeMcpSessionRegistry.setFusionRole(input) : Effect.void;
131293
+ const clearActiveMcpFusionRole = (input) => activeMcpSessionRegistry ? activeMcpSessionRegistry.clearFusionRole(input) : Effect.void;
130951
131294
  const revokeActiveMcpWatchThread = (input) => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeWatchThread(input) : Effect.void;
130952
131295
  const grantActiveMcpAdviseThread = (input) => activeMcpSessionRegistry ? activeMcpSessionRegistry.grantAdviseThread(input) : Effect.void;
130953
131296
  const revokeActiveMcpAdviseThread = (input) => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeAdviseThread(input) : Effect.void;
@@ -131069,6 +131412,15 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
131069
131412
  return Option.isSome(thread) ? resolveProjectAgentBrowserAccess(settings, thread.value.projectId) : false;
131070
131413
  })), Effect.catch((cause) => Effect.logWarning("Could not read settings; withholding agent browser access.", { cause }).pipe(Effect.as(false))));
131071
131414
  const computerUseAccess = yield* registry.getInstanceInfo(providerInstanceId).pipe(Effect.map((info) => info.driverKind !== "codex"), Effect.catch(() => Effect.succeed(false)));
131415
+ const fusionRole = Option.isSome(projectionQuery) ? yield* projectionQuery.value.getActiveThreadPairs().pipe(Effect.map((pairs) => {
131416
+ const pair = pairs.find((entry) => entry.watcherThreadId === threadId || entry.implementerThreadId === threadId);
131417
+ if (pair === void 0) return void 0;
131418
+ return pair.watcherThreadId === threadId ? "watcher" : "implementer";
131419
+ }), Effect.mapError((cause) => new ProviderValidationError({
131420
+ operation: "ProviderService.prepareMcpSession",
131421
+ issue: "Could not read thread pairs, so the session's Fusion role is unknown. Refusing to issue an MCP credential that would carry no role.",
131422
+ cause
131423
+ }))) : yield* Effect.logWarning("No projection service, so an MCP credential is issued with no Fusion role. A pair whose event this process never saw will be ungated.", { threadId }).pipe(Effect.as(void 0));
131072
131424
  const threadTitle = Option.isSome(projectionQuery) ? yield* projectionQuery.value.getThreadShellById(threadId).pipe(Effect.map((thread) => Option.isSome(thread) ? thread.value.title : void 0), Effect.catch(() => Effect.succeed(void 0))) : void 0;
131073
131425
  const credential = yield* issueActiveMcpCredential({
131074
131426
  threadId,
@@ -131077,7 +131429,8 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
131077
131429
  browserAccess,
131078
131430
  computerUseAccess,
131079
131431
  ...watchThreadIds !== void 0 ? { watchThreadIds } : {},
131080
- ...adviseThreadIds !== void 0 ? { adviseThreadIds } : {}
131432
+ ...adviseThreadIds !== void 0 ? { adviseThreadIds } : {},
131433
+ ...fusionRole !== void 0 ? { fusionRole } : {}
131081
131434
  });
131082
131435
  if (credential) yield* Effect.sync(() => setMcpProviderSession(credential.config));
131083
131436
  return credential;
@@ -131923,6 +132276,47 @@ const mcpInputSchema = (schema) => ({
131923
132276
  ...schema,
131924
132277
  type: "object"
131925
132278
  });
132279
+ const P4_TOOL_NAMES = new Set(P4CODE_MCP_TOOL_NAMES);
132280
+ /**
132281
+ * The server's own answer to "may this role call this tool", applied to every
132282
+ * registered P4 tool before its handler runs.
132283
+ *
132284
+ * The adapters also hide a role's forbidden tools, but that is a context
132285
+ * measure: it depends on a user setting and on the one adapter that can filter
132286
+ * a session's tool list. A caller that never saw the tool can still name it,
132287
+ * so the rule that matters is enforced here, where every provider's call
132288
+ * arrives and neither a setting nor a prompt can move it.
132289
+ *
132290
+ * Only P4's own tools are governed. A name outside the catalogue belongs to
132291
+ * another server sharing this registration helper - Computer Use is one - and
132292
+ * this policy has nothing to say about it.
132293
+ */
132294
+ const enforceFusionRole = (toolName) => Effect.serviceOption(McpInvocationContext).pipe(Effect.map((invocation) => {
132295
+ if (!P4_TOOL_NAMES.has(toolName)) return null;
132296
+ const role = invocation._tag === "Some" ? invocation.value.fusionRole : void 0;
132297
+ if (role === void 0 || fusionRoleMayCallP4Tool(role, toolName)) return null;
132298
+ return role;
132299
+ }));
132300
+ /** The half's own word, so the message names the caller rather than a code. */
132301
+ const ROLE_LABEL = {
132302
+ implementer: "builder",
132303
+ watcher: "supervisor"
132304
+ };
132305
+ const deniedResult = (toolName, role) => new McpSchema.CallToolResult({
132306
+ isError: true,
132307
+ content: [{
132308
+ type: "text",
132309
+ text: `${toolName} is not a tool the ${ROLE_LABEL[role]} half of a Fusion pair may call. Report the missing tool rather than working around it.`
132310
+ }]
132311
+ });
132312
+ /**
132313
+ * Wraps one tool handler in the role gate.
132314
+ *
132315
+ * Exported because `addTool` is reachable from two places: this helper, and
132316
+ * the one tool registered by hand. A gate that only covered the helper would
132317
+ * leave that tool open, which is the kind of hole a policy is judged by.
132318
+ */
132319
+ const withFusionRoleGate = (toolName, handle) => (payload) => enforceFusionRole(toolName).pipe(Effect.flatMap((deniedRole) => deniedRole === null ? handle(payload) : Effect.succeed(deniedResult(toolName, deniedRole))));
131926
132320
  const mcpToolkit = (toolkit) => Layer.effectDiscard(Effect.gen(function* () {
131927
132321
  const server = yield* McpServer.McpServer;
131928
132322
  yield* McpServer.registerToolkit(toolkit).pipe(Effect.provideService(McpServer.McpServer, {
@@ -131932,7 +132326,8 @@ const mcpToolkit = (toolkit) => Layer.effectDiscard(Effect.gen(function* () {
131932
132326
  tool: new McpSchema.Tool({
131933
132327
  ...options.tool,
131934
132328
  inputSchema: mcpInputSchema(options.tool.inputSchema)
131935
- })
132329
+ }),
132330
+ handle: withFusionRoleGate(options.tool.name, options.handle)
131936
132331
  })
131937
132332
  }));
131938
132333
  }));
@@ -132221,13 +132616,13 @@ const registerScreenshot = Effect.fn("device.registerScreenshot")(function* () {
132221
132616
  }
132222
132617
  }),
132223
132618
  annotations: DeviceScreenshotTool.annotations,
132224
- handle: (payload) => Effect.withFiber((fiber) => built.handle("device_screenshot", payload).pipe(Stream.unwrap, Stream.run(Sink.last()), Effect.flatMap(Effect.fromOption), Effect.provideService(DeviceService, service), Effect.provideService(DeviceScreenshot, screenshots), Effect.provideService(McpInvocationContext, Context.getUnsafe(fiber.context, McpInvocationContext)), Effect.flatMap((result) => screenshotContent(result.encodedResult)), Effect.orElseSucceed(() => new McpSchema.CallToolResult({
132619
+ handle: withFusionRoleGate(DeviceScreenshotTool.name, (payload) => Effect.withFiber((fiber) => built.handle("device_screenshot", payload).pipe(Stream.unwrap, Stream.run(Sink.last()), Effect.flatMap(Effect.fromOption), Effect.provideService(DeviceService, service), Effect.provideService(DeviceScreenshot, screenshots), Effect.provideService(McpInvocationContext, Context.getUnsafe(fiber.context, McpInvocationContext)), Effect.flatMap((result) => screenshotContent(result.encodedResult)), Effect.orElseSucceed(() => new McpSchema.CallToolResult({
132225
132620
  isError: true,
132226
132621
  content: [{
132227
132622
  type: "text",
132228
132623
  text: "Device screenshot unavailable. Check the open session and refresh devices."
132229
132624
  }]
132230
- }))))
132625
+ })))))
132231
132626
  });
132232
132627
  });
132233
132628
  const DeviceToolkitRegistrationLive = Layer.mergeAll(mcpToolkit(DeviceStandardToolkit), Layer.effectDiscard(registerScreenshot())).pipe(Layer.provide(DeviceToolkitHandlersLive));
@@ -132398,7 +132793,7 @@ const stringField = (record, key) => {
132398
132793
  const value = record[key];
132399
132794
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
132400
132795
  };
132401
- const make$10 = Effect.gen(function* () {
132796
+ const make$12 = Effect.gen(function* () {
132402
132797
  const linear = yield* LinearClient;
132403
132798
  return { resolve: Effect.fn("TicketResolver.resolve")(function* (reference) {
132404
132799
  const identifier = parseTicketReference(reference);
@@ -132429,7 +132824,7 @@ const make$10 = Effect.gen(function* () {
132429
132824
  };
132430
132825
  }) };
132431
132826
  });
132432
- const layer$5 = Layer.effect(TicketResolver, make$10);
132827
+ const layer$5 = Layer.effect(TicketResolver, make$12);
132433
132828
  //#endregion
132434
132829
  //#region src/mcp/toolkits/tasks/tools.ts
132435
132830
  const dependencies = [McpInvocationContext, TaskRepository];
@@ -132968,7 +133363,7 @@ const AskUserQuestionTool = Tool.make("ask_user_question", {
132968
133363
  ]
132969
133364
  }).annotate(Tool.Title, "Ask user question").annotate(Tool.Readonly, false).annotate(Tool.Destructive, false).annotate(Tool.Idempotent, false);
132970
133365
  const ThreadSpawnTool = Tool.make("thread_spawn", {
132971
- description: "Start a new agent thread and send it a first message, then return its id. Use it to hand a piece of work to a fresh thread - a subtask you just planned, a job that wants its own transcript. Set fusion true to create two new threads as a separate Fusion pair and send prompt to its supervisor, which plans and directs the builder later, even when this thread already belongs to a pair. Set fusionWatcher true only for a supervisor to pair with the current thread. Both use saved Fusion role models unless overridden. Act on the user's request; no special command syntax is required. The new thread inherits this one's project, model, permission mode, interaction mode, compression and subagent policy, and every one of those can be set here instead - what you set is already in force for the first turn, so a thread never has to be corrected after it starts. Set modelSelection to run it on another model: give the model id, and leave instanceId out to keep this thread's provider instance. Once started it can be watched with thread_watch_events and changed later with thread_configure. A thread that was itself started this way cannot start another.",
133366
+ description: "Start a new agent thread and send it a first message, then return its id. Use it to hand a piece of work to a fresh thread - a subtask you just planned, a job that wants its own transcript. For activation here or a new Fusion in this chat, use thread_pair_create with no watcherThreadId so this chat becomes Supervisor and its new Builder starts empty. Set fusion true only for an explicitly separate pair to create two new threads and send prompt to its supervisor, which plans and directs the builder later, even when this thread already belongs to a pair. Set fusionWatcher true only for legacy current-chat-as-builder pairing. Both use saved Fusion role models unless overridden. Act on the user's request; no special command syntax is required. The new thread inherits this one's project, model, permission mode, interaction mode, compression and subagent policy, and every one of those can be set here instead - what you set is already in force for the first turn, so a thread never has to be corrected after it starts. Set modelSelection to run it on another model: give the model id, and leave instanceId out to keep this thread's provider instance. Once started it can be watched with thread_watch_events and changed later with thread_configure. A thread that was itself started this way cannot start another.",
132972
133367
  parameters: ThreadSpawnInput,
132973
133368
  success: ThreadSpawnResult,
132974
133369
  failure: ThreadControlToolError,
@@ -132982,18 +133377,22 @@ const ThreadSpawnTool = Tool.make("thread_spawn", {
132982
133377
  ]
132983
133378
  }).annotate(Tool.Title, "Start a thread").annotate(Tool.Readonly, false).annotate(Tool.Destructive, false).annotate(Tool.Idempotent, false);
132984
133379
  const ThreadPairCreateTool = Tool.make("thread_pair_create", {
132985
- description: "Create a persisted Fusion pair between this agent session's own thread as builder and a supervisor it previously created with thread_spawn. Act on the user's request; no special command syntax is required. The current thread must not already belong to an active Fusion pair. Successful creation automatically continues the builder, so never ask the user to resend the task.",
133380
+ description: "Activate Fusion here: call with no watcherThreadId to retain this chat and its history as Supervisor and create a new Builder with no messages or provider context. Builder stays idle until you send thread_advise. Use the returned implementerThreadId for the phase plan and advice. No transcript is copied. Requires explicit user authorization and a standalone current thread. Only an explicitly separate pair should use thread_spawn with fusion true. Supplying watcherThreadId retains legacy current-chat-as-builder pairing.",
132986
133381
  parameters: ThreadPairCreateInput,
132987
133382
  success: ThreadPairCreateResult,
132988
133383
  failure: ThreadControlToolError,
132989
133384
  dependencies: [
132990
133385
  McpInvocationContext,
132991
133386
  OrchestrationEngineService,
133387
+ McpSessionRegistry,
133388
+ ProjectionThreadRepository,
133389
+ ProjectionSnapshotQuery,
133390
+ ServerSettingsService,
132992
133391
  Crypto.Crypto
132993
133392
  ]
132994
133393
  }).annotate(Tool.Title, "Create a Fusion pair").annotate(Tool.Readonly, false).annotate(Tool.Destructive, false).annotate(Tool.Idempotent, false);
132995
133394
  const ThreadPairDetachTool = Tool.make("thread_pair_detach", {
132996
- description: "Detach this agent session's active Fusion pair, stopping future automatic supervisor reviews while preserving both threads. The session must be the pair's implementer and must have created its supervisor through thread_spawn.",
133395
+ description: "Detach this agent session's active Fusion pair, stopping future automatic supervisor reviews while preserving both threads. The session must belong to the pair and must have created its other thread.",
132997
133396
  parameters: ThreadPairDetachInput,
132998
133397
  success: ThreadPairDetachResult,
132999
133398
  failure: ThreadControlToolError,
@@ -133383,6 +133782,14 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
133383
133782
  watcherThreadId: fusionPair.watcherThreadId,
133384
133783
  createdAt
133385
133784
  }, threadId);
133785
+ yield* registry.setFusionRole({
133786
+ threadId,
133787
+ role: "implementer"
133788
+ });
133789
+ yield* registry.setFusionRole({
133790
+ threadId: fusionPair.watcherThreadId,
133791
+ role: "watcher"
133792
+ });
133386
133793
  }
133387
133794
  yield* dispatchControl({
133388
133795
  type: "thread.turn.start",
@@ -133410,6 +133817,74 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
133410
133817
  };
133411
133818
  }),
133412
133819
  thread_pair_create: (input) => Effect.gen(function* () {
133820
+ if (input.watcherThreadId === void 0) {
133821
+ const invocation = yield* requireThreadSpawn(true);
133822
+ const reject = (detail) => new ThreadControlRejectedError({
133823
+ threadId: invocation.threadId,
133824
+ commandType: "thread-pair.create",
133825
+ detail
133826
+ });
133827
+ if ((yield* (yield* ProjectionSnapshotQuery).getCommandReadModel().pipe(Effect.mapError((cause) => reject(cause.message)))).threadPairs?.some((pair) => pair.detachedAt === null && (pair.implementerThreadId === invocation.threadId || pair.watcherThreadId === invocation.threadId))) return yield* reject("Current thread already belongs to an active Fusion pair.");
133828
+ const current = yield* (yield* ProjectionThreadRepository).getById({ threadId: invocation.threadId }).pipe(Effect.mapError((cause) => reject(cause.message)));
133829
+ if (Option.isNone(current) || current.value.deletedAt !== null) return yield* reject("Current thread does not exist.");
133830
+ const settings = yield* (yield* ServerSettingsService).getSettings.pipe(Effect.mapError((cause) => reject(cause.message)));
133831
+ const crypto = yield* Crypto.Crypto;
133832
+ const implementerThreadId = ThreadId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
133833
+ const pairId = ThreadPairId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
133834
+ const createdAt = DateTime.formatIso(yield* DateTime.now);
133835
+ const template = current.value;
133836
+ yield* dispatchControl({
133837
+ type: "thread.create",
133838
+ commandId: yield* newCommandId,
133839
+ threadId: implementerThreadId,
133840
+ projectId: template.projectId,
133841
+ title: `Implement: ${template.title}`,
133842
+ modelSelection: settings.fusionBuilderModelSelection ?? template.modelSelection,
133843
+ runtimeMode: template.runtimeMode,
133844
+ interactionMode: template.interactionMode,
133845
+ compressMode: template.compressMode,
133846
+ unpromptedSubagents: template.unpromptedSubagents === 1,
133847
+ branch: template.branch,
133848
+ worktreePath: template.worktreePath,
133849
+ createdAt
133850
+ }, invocation.threadId);
133851
+ yield* dispatchControl({
133852
+ type: "thread-pair.create",
133853
+ commandId: yield* newCommandId,
133854
+ pairId,
133855
+ implementerThreadId,
133856
+ watcherThreadId: invocation.threadId,
133857
+ ...input.gateTimeoutMs === void 0 ? {} : { gateTimeoutMs: input.gateTimeoutMs },
133858
+ createdAt
133859
+ }, invocation.threadId);
133860
+ const registry = yield* McpSessionRegistry;
133861
+ yield* registry.recordSpawnedThread({
133862
+ providerSessionId: invocation.providerSessionId,
133863
+ threadId: implementerThreadId
133864
+ });
133865
+ yield* registry.setFusionRole({
133866
+ threadId: implementerThreadId,
133867
+ role: "implementer"
133868
+ });
133869
+ yield* registry.setFusionRole({
133870
+ threadId: invocation.threadId,
133871
+ role: "watcher"
133872
+ });
133873
+ const grant = {
133874
+ watcherThreadId: invocation.threadId,
133875
+ watchedThreadId: implementerThreadId
133876
+ };
133877
+ yield* registry.grantWatchThread(grant);
133878
+ yield* registry.grantAdviseThread({
133879
+ watcherThreadId: invocation.threadId,
133880
+ advisedThreadId: implementerThreadId
133881
+ });
133882
+ return {
133883
+ pairId,
133884
+ implementerThreadId,
133885
+ watcherThreadId: invocation.threadId
133886
+ };
133887
+ }
133413
133888
  const { invocation, threadId: watcherThreadId } = yield* requireThreadControlTarget(input.watcherThreadId);
133414
133889
  const crypto = yield* Crypto.Crypto;
133415
133890
  const pairId = ThreadPairId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
@@ -133423,6 +133898,15 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
133423
133898
  continuationMessageId,
133424
133899
  createdAt: DateTime.formatIso(yield* DateTime.now)
133425
133900
  }, invocation.threadId);
133901
+ const registry = yield* McpSessionRegistry;
133902
+ yield* registry.setFusionRole({
133903
+ threadId: invocation.threadId,
133904
+ role: "implementer"
133905
+ });
133906
+ yield* registry.setFusionRole({
133907
+ threadId: watcherThreadId,
133908
+ role: "watcher"
133909
+ });
133426
133910
  return {
133427
133911
  pairId,
133428
133912
  implementerThreadId: invocation.threadId,
@@ -133442,15 +133926,16 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
133442
133926
  commandType: "thread-pair.detach",
133443
133927
  detail: `Active Fusion pair '${input.pairId}' was not found.`
133444
133928
  });
133445
- if (pair.implementerThreadId !== invocation.threadId) return yield* new ThreadControlRejectedError({
133929
+ if (pair.implementerThreadId !== invocation.threadId && pair.watcherThreadId !== invocation.threadId) return yield* new ThreadControlRejectedError({
133446
133930
  threadId: invocation.threadId,
133447
133931
  commandType: "thread-pair.detach",
133448
- detail: "This session is not the pair's implementer."
133932
+ detail: "This session is not a member of the pair."
133449
133933
  });
133450
- if (!invocation.controlThreadIds?.has(pair.watcherThreadId)) return yield* new ThreadControlRejectedError({
133934
+ const otherThreadId = pair.implementerThreadId === invocation.threadId ? pair.watcherThreadId : pair.implementerThreadId;
133935
+ if (!invocation.controlThreadIds?.has(otherThreadId)) return yield* new ThreadControlRejectedError({
133451
133936
  threadId: invocation.threadId,
133452
133937
  commandType: "thread-pair.detach",
133453
- detail: "This session did not create the pair's supervisor with thread_spawn."
133938
+ detail: "This session did not create the other thread in the pair."
133454
133939
  });
133455
133940
  const detachedAt = DateTime.formatIso(yield* DateTime.now);
133456
133941
  yield* dispatchControl({
@@ -134067,6 +134552,20 @@ var TypeSafeRequestError = class extends Data.TaggedError("TypeSafeRequestError"
134067
134552
  var TypeSafeClient = class extends Context.Service()("@p4code/cli/orchestration/triage/TypeSafeClient") {};
134068
134553
  const asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
134069
134554
  /**
134555
+ * The finite numbers out of a probability map, or nothing.
134556
+ *
134557
+ * Non-numeric and non-finite entries are dropped rather than coerced, for the
134558
+ * same reason a missing confidence is never read as zero: a rank built on an
134559
+ * invented number is worse than one built on fewer.
134560
+ */
134561
+ function readProbabilities(value) {
134562
+ const record = asRecord(value);
134563
+ if (record === void 0) return void 0;
134564
+ const probabilities = {};
134565
+ for (const [key, entry] of Object.entries(record)) if (typeof entry === "number" && Number.isFinite(entry)) probabilities[key] = entry;
134566
+ return Object.keys(probabilities).length === 0 ? void 0 : probabilities;
134567
+ }
134568
+ /**
134070
134569
  * Reads one answer, or nothing.
134071
134570
  *
134072
134571
  * A `choice` or `score` without a numeric `confidence` is rejected rather than
@@ -134081,10 +134580,16 @@ function parseTypeSafeAnswer(value) {
134081
134580
  case "choice": {
134082
134581
  const choice = record["choice"];
134083
134582
  if (typeof choice !== "string" || typeof confidence !== "number") return void 0;
134084
- return {
134583
+ const probabilities = readProbabilities(record["probabilities"]);
134584
+ return probabilities === void 0 ? {
134085
134585
  type: "choice",
134086
134586
  choice,
134087
134587
  confidence
134588
+ } : {
134589
+ type: "choice",
134590
+ choice,
134591
+ confidence,
134592
+ probabilities
134088
134593
  };
134089
134594
  }
134090
134595
  case "score": {
@@ -134225,7 +134730,7 @@ const TypeSafeToolkit = Toolkit.make(TypeSafeAskTool);
134225
134730
  /** Headroom for the envelope and for the estimate's own error. */
134226
134731
  const RESERVE_TOKENS = 2e3;
134227
134732
  /** One question per call, so the id is fixed rather than invented. */
134228
- const QUESTION_ID = "question";
134733
+ const QUESTION_ID$1 = "question";
134229
134734
  const requestTokens = (input) => estimateTokens(input.data) + estimateTokens(input.instructions) + estimateTokens(JSON.stringify(input.criteria));
134230
134735
  const TypeSafeToolkitHandlersLive = TypeSafeToolkit.toLayer({ typesafe_ask: (input) => Effect.gen(function* () {
134231
134736
  const tokens = requestTokens(input);
@@ -134239,7 +134744,7 @@ const TypeSafeToolkitHandlersLive = TypeSafeToolkit.toLayer({ typesafe_ask: (inp
134239
134744
  model: settings?.fusionReviewTriage.triageModel ?? "jev-latest",
134240
134745
  state: input.data,
134241
134746
  question: {
134242
- id: QUESTION_ID,
134747
+ id: QUESTION_ID$1,
134243
134748
  type: input.type,
134244
134749
  instructions: input.instructions,
134245
134750
  criteria: input.criteria
@@ -134268,6 +134773,16 @@ const TypeSafeToolkitHandlersLive = TypeSafeToolkit.toLayer({ typesafe_ask: (inp
134268
134773
  * appending beside it.
134269
134774
  */
134270
134775
  const evidenceReadActivityId = (watchedThreadId, throughSequence) => EventId.make(`fusion-evidence:${watchedThreadId}:${throughSequence}`);
134776
+ /**
134777
+ * The key a raw read records under, when the supervisor paged the events itself.
134778
+ *
134779
+ * A raw read has no fixed bound to key on: the supervisor discovers where the
134780
+ * window ends by paging to it, so keying on the sequence it happens to have
134781
+ * reached would mint one row per page. The turn is the bound that already
134782
+ * exists, and it collapses the whole raw read into the single row a compact
134783
+ * review would have written.
134784
+ */
134785
+ const rawEvidenceReadActivityId = (watchedThreadId, readerTurnId) => EventId.make(`fusion-evidence:${watchedThreadId}:turn:${readerTurnId}`);
134271
134786
  /** One report per review, keyed like the read it reports on so a retry replaces. */
134272
134787
  const evidenceReportActivityId = (watchedThreadId, throughSequence) => EventId.make(`fusion-evidence-summary:${watchedThreadId}:${throughSequence}`);
134273
134788
  /**
@@ -134300,7 +134815,8 @@ const evidenceTiming = (readAt, closedAt) => {
134300
134815
  const correlateEvidence = Effect.fn("fusionEvidenceReport.correlateEvidence")(function* (input) {
134301
134816
  const projections = yield* ProjectionSnapshotQuery;
134302
134817
  const latest = yield* projections.getLatestThreadContextWindow(input.readerThreadId).pipe(Effect.orElseSucceed(() => Option.none()));
134303
- const read = yield* projections.getEvidenceReadContext(input.readActivityId).pipe(Effect.orElseSucceed(() => Option.none()));
134818
+ const readContext = (activityId) => projections.getEvidenceReadContext(activityId).pipe(Effect.orElseSucceed(() => Option.none()));
134819
+ const read = yield* readContext(input.readActivityId).pipe(Effect.flatMap((row) => Option.isSome(row) || input.fallbackReadActivityId === void 0 ? Effect.succeed(row) : readContext(input.fallbackReadActivityId)));
134304
134820
  const timing = Option.match(read, {
134305
134821
  onNone: () => null,
134306
134822
  onSome: (row) => evidenceTiming(row.readAt, input.closedAt)
@@ -134341,8 +134857,14 @@ const ThreadWatchEventsTool = Tool.make("thread_watch_events", {
134341
134857
  parameters: ThreadWatchEventsInput,
134342
134858
  success: ThreadWatchEventsResult,
134343
134859
  failure: ThreadWatchToolError,
134344
- dependencies: [McpInvocationContext, ThreadEventStreamService]
134345
- }).annotate(Tool.Title, "Read watched thread events").annotate(Tool.Readonly, true).annotate(Tool.Destructive, false).annotate(Tool.Idempotent, true);
134860
+ dependencies: [
134861
+ McpInvocationContext,
134862
+ ThreadEventStreamService,
134863
+ OrchestrationEngineService,
134864
+ ProjectionSnapshotQuery,
134865
+ Crypto.Crypto
134866
+ ]
134867
+ }).annotate(Tool.Title, "Read watched thread events").annotate(Tool.Readonly, false).annotate(Tool.Destructive, false).annotate(Tool.Idempotent, true);
134346
134868
  const ThreadWatchReviewTool = Tool.make("thread_watch_review", {
134347
134869
  description: "Read bounded compact review evidence for an explicitly granted thread. Combines assistant fragments by message identity; preserves user requirements, tool evidence, plans, errors and turn boundaries. Resume with nextAfterSequence and the same throughSequence until hasMore is false, even on empty pages. Message operations append or replace by identity across pages. Truncated evidence includes source sequence ranges for targeted recovery with thread_watch_events; do not treat an excerpt as full proof.",
134348
134870
  parameters: ThreadWatchReviewInput,
@@ -134394,20 +134916,24 @@ const REPORT_HEADING = {
134394
134916
  * for; losing the note that it happened is a smaller loss than losing the page,
134395
134917
  * so a rejected dispatch is logged and the page is returned anyway.
134396
134918
  */
134397
- const recordEvidenceRead = Effect.fn("mcp.watch.recordEvidenceRead")(function* (readerThreadId, page, afterSequence) {
134919
+ const recordEvidenceRead = Effect.fn("mcp.watch.recordEvidenceRead")(function* (readerThreadId, window) {
134398
134920
  const engine = yield* OrchestrationEngineService;
134399
134921
  const crypto = yield* Crypto.Crypto;
134400
134922
  const projections = yield* ProjectionSnapshotQuery;
134401
134923
  const createdAt = DateTime.formatIso(yield* DateTime.now);
134402
- const recorded = yield* projections.getEvidenceReadContext(evidenceReadActivityId(page.threadId, page.throughSequence)).pipe(Effect.orElseSucceed(() => Option.none()));
134924
+ const recorded = yield* projections.getEvidenceReadContext(window.activityId).pipe(Effect.orElseSucceed(() => Option.none()));
134403
134925
  const opened = Option.isSome(recorded);
134404
134926
  const readCreatedAt = Option.match(recorded, {
134405
134927
  onNone: () => createdAt,
134406
134928
  onSome: (row) => row.readAt
134407
134929
  });
134408
134930
  const readAfterSequence = Option.match(recorded, {
134409
- onNone: () => afterSequence,
134410
- onSome: (row) => row.afterSequence ?? afterSequence
134931
+ onNone: () => window.afterSequence,
134932
+ onSome: (row) => row.afterSequence ?? window.afterSequence
134933
+ });
134934
+ const readThroughSequence = Option.match(recorded, {
134935
+ onNone: () => window.throughSequence,
134936
+ onSome: (row) => row.throughSequence === null ? window.throughSequence : Math.max(row.throughSequence, window.throughSequence)
134411
134937
  });
134412
134938
  const activeTurnId = yield* projections.getActiveTurnId(readerThreadId).pipe(Effect.orElseSucceed(() => Option.none()), Effect.map(Option.getOrNull));
134413
134939
  const usedTokensAtRead = opened ? Option.getOrNull(Option.map(recorded, (row) => row.usedTokensAtRead)) : yield* projections.getLatestThreadContextWindow(readerThreadId).pipe(Effect.map(Option.map((row) => row.usedTokens)), Effect.orElseSucceed(() => Option.none()), Effect.map(Option.getOrNull));
@@ -134417,29 +134943,40 @@ const recordEvidenceRead = Effect.fn("mcp.watch.recordEvidenceRead")(function* (
134417
134943
  threadId: readerThreadId,
134418
134944
  createdAt,
134419
134945
  activity: {
134420
- id: evidenceReadActivityId(page.threadId, page.throughSequence),
134946
+ id: window.activityId,
134421
134947
  createdAt: readCreatedAt,
134422
134948
  tone: "info",
134423
134949
  kind: "fusion.evidence.read",
134424
134950
  summary: "Read builder evidence",
134425
134951
  turnId: null,
134426
134952
  payload: {
134427
- watchedThreadId: page.threadId,
134953
+ watchedThreadId: window.watchedThreadId,
134428
134954
  afterSequence: readAfterSequence,
134429
- throughSequence: page.throughSequence,
134430
- nextAfterSequence: page.nextAfterSequence,
134431
- hasMore: page.hasMore,
134432
- scannedEvents: page.scannedEvents,
134955
+ throughSequence: readThroughSequence,
134956
+ nextAfterSequence: window.nextAfterSequence,
134957
+ hasMore: window.hasMore,
134958
+ scannedEvents: window.scannedEvents,
134433
134959
  ...activeTurnId === null ? {} : { turnId: activeTurnId },
134960
+ ...window.raw === true ? { raw: true } : {},
134434
134961
  ...usedTokensAtRead === null ? {} : { usedTokensAtRead }
134435
134962
  }
134436
134963
  }
134437
134964
  }).pipe(Effect.catchCause((cause) => Effect.logWarning("could not record the supervisor's evidence read", {
134438
134965
  readerThreadId,
134439
- watchedThreadId: page.threadId,
134966
+ watchedThreadId: window.watchedThreadId,
134440
134967
  cause
134441
134968
  })));
134442
134969
  });
134970
+ /**
134971
+ * Whether this reader is the watcher half of a live pair over this thread.
134972
+ *
134973
+ * `thread_watch_events` is the generic polling tool, so an orchestrator reading
134974
+ * a thread it spawned goes through it too. Only a Fusion watcher is reading
134975
+ * builder evidence, and only its work log should grow a row saying so.
134976
+ */
134977
+ const watchesAsFusionSupervisor = Effect.fn("mcp.watch.watchesAsFusionSupervisor")(function* (readerThreadId, watchedThreadId) {
134978
+ return (yield* (yield* ProjectionSnapshotQuery).getActiveThreadPairs().pipe(Effect.orElseSucceed(() => []))).some((pair) => pair.watcherThreadId === readerThreadId && pair.implementerThreadId === watchedThreadId);
134979
+ });
134443
134980
  /** Bounded so one report cannot dominate a thread's activity storage. */
134444
134981
  const clip = (value, limit) => {
134445
134982
  const trimmed = value?.trim();
@@ -134453,7 +134990,15 @@ const WatchToolkitHandlersLive = WatchToolkit.toLayer({
134453
134990
  threadId: input.threadId,
134454
134991
  detail: cause.message
134455
134992
  })));
134456
- yield* recordEvidenceRead(invocation.threadId, page, input.afterSequence ?? 0);
134993
+ yield* recordEvidenceRead(invocation.threadId, {
134994
+ activityId: evidenceReadActivityId(page.threadId, page.throughSequence),
134995
+ watchedThreadId: page.threadId,
134996
+ afterSequence: input.afterSequence ?? 0,
134997
+ throughSequence: page.throughSequence,
134998
+ nextAfterSequence: page.nextAfterSequence,
134999
+ hasMore: page.hasMore,
135000
+ scannedEvents: page.scannedEvents
135001
+ });
134457
135002
  return page;
134458
135003
  }),
134459
135004
  thread_evidence_report: (input) => Effect.gen(function* () {
@@ -134461,9 +135006,11 @@ const WatchToolkitHandlersLive = WatchToolkit.toLayer({
134461
135006
  const engine = yield* OrchestrationEngineService;
134462
135007
  const crypto = yield* Crypto.Crypto;
134463
135008
  const createdAt = DateTime.formatIso(yield* DateTime.now);
135009
+ const activeTurnId = yield* (yield* ProjectionSnapshotQuery).getActiveTurnId(invocation.threadId).pipe(Effect.orElseSucceed(() => Option.none()), Effect.map(Option.getOrNull));
134464
135010
  const { context, timing } = yield* correlateEvidence({
134465
135011
  readerThreadId: invocation.threadId,
134466
135012
  readActivityId: evidenceReadActivityId(input.threadId, input.throughSequence),
135013
+ ...activeTurnId === null ? {} : { fallbackReadActivityId: rawEvidenceReadActivityId(input.threadId, activeTurnId) },
134467
135014
  closedAt: createdAt
134468
135015
  });
134469
135016
  const summary = clip(input.summary, THREAD_EVIDENCE_REPORT_MAX_CHARS);
@@ -134504,7 +135051,7 @@ const WatchToolkitHandlersLive = WatchToolkit.toLayer({
134504
135051
  };
134505
135052
  }),
134506
135053
  thread_watch_events: (input) => Effect.gen(function* () {
134507
- yield* requireWatchCapability(input.threadId);
135054
+ const invocation = yield* requireWatchCapability(input.threadId);
134508
135055
  const page = yield* (yield* ThreadEventStreamService).read({
134509
135056
  threadId: input.threadId,
134510
135057
  ...input.afterSequence !== void 0 ? { afterSequence: input.afterSequence } : {},
@@ -134513,6 +135060,28 @@ const WatchToolkitHandlersLive = WatchToolkit.toLayer({
134513
135060
  threadId: input.threadId,
134514
135061
  detail: cause.message
134515
135062
  })));
135063
+ const lastSequence = page.events.at(-1)?.sequence;
135064
+ if (lastSequence !== void 0 && (yield* watchesAsFusionSupervisor(invocation.threadId, input.threadId))) {
135065
+ const projections = yield* ProjectionSnapshotQuery;
135066
+ const turnId = yield* projections.getActiveTurnId(invocation.threadId).pipe(Effect.orElseSucceed(() => Option.none()), Effect.map(Option.getOrNull));
135067
+ if (turnId !== null) {
135068
+ const activityId = rawEvidenceReadActivityId(input.threadId, turnId);
135069
+ const opened = yield* projections.getLatestEvidenceRead(invocation.threadId).pipe(Effect.orElseSucceed(() => Option.none()));
135070
+ if (!Option.match(opened, {
135071
+ onNone: () => false,
135072
+ onSome: (row) => row.turnId === turnId && row.watchedThreadId === input.threadId
135073
+ })) yield* recordEvidenceRead(invocation.threadId, {
135074
+ activityId,
135075
+ watchedThreadId: input.threadId,
135076
+ afterSequence: input.afterSequence ?? 0,
135077
+ throughSequence: lastSequence,
135078
+ nextAfterSequence: lastSequence,
135079
+ hasMore: page.hasMore,
135080
+ scannedEvents: page.events.length,
135081
+ raw: true
135082
+ });
135083
+ }
135084
+ }
134516
135085
  return {
134517
135086
  threadId: input.threadId,
134518
135087
  events: page.events,
@@ -134590,7 +135159,7 @@ const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot
134590
135159
  }
134591
135160
  }),
134592
135161
  annotations: tool.annotations,
134593
- handle: (payload) => Effect.withFiber((fiber) => {
135162
+ handle: withFusionRoleGate(tool.name, (payload) => Effect.withFiber((fiber) => {
134594
135163
  const invocation = Context.getUnsafe(fiber.context, McpInvocationContext);
134595
135164
  return built.handle("preview_snapshot", payload).pipe(Stream.unwrap, Stream.run(Sink.last()), Effect.flatMap(Effect.fromOption), Effect.provideService(PreviewAutomationBroker, broker), Effect.provideService(McpInvocationContext, invocation), Effect.matchCauseEffect({
134596
135165
  onFailure: previewSnapshotFailure,
@@ -134619,7 +135188,7 @@ const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot
134619
135188
  }));
134620
135189
  }
134621
135190
  }));
134622
- })
135191
+ }))
134623
135192
  });
134624
135193
  });
134625
135194
  const PreviewStandardToolkitRegistrationLive = mcpToolkit(PreviewStandardToolkit).pipe(Layer.provide(PreviewStandardToolkitHandlersLive));
@@ -135840,7 +136409,7 @@ const COMPLETION_SWEEP_BATCH_SIZE = 25;
135840
136409
  const COMPLETION_SWEEP_INTERVAL = Duration.minutes(1);
135841
136410
  const COMPLETION_PR_TIMEOUT = Duration.seconds(10);
135842
136411
  const MILLISECONDS_PER_DAY = 1440 * 60 * 1e3;
135843
- const make$9 = Effect.gen(function* () {
136412
+ const make$11 = Effect.gen(function* () {
135844
136413
  const sql = yield* SqlClient.SqlClient;
135845
136414
  const settings = yield* ServerSettingsService;
135846
136415
  const git = yield* GitManager;
@@ -135927,7 +136496,7 @@ const make$9 = Effect.gen(function* () {
135927
136496
  };
135928
136497
  });
135929
136498
  var ThreadCompletionReactor = class extends Context.Service()("@p4code/cli/orchestration/Layers/ThreadCompletionReactor") {};
135930
- const ThreadCompletionReactorLive = Layer.effect(ThreadCompletionReactor, make$9);
136499
+ const ThreadCompletionReactorLive = Layer.effect(ThreadCompletionReactor, make$11);
135931
136500
  //#endregion
135932
136501
  //#region src/orchestration/Services/ScheduledTaskReactor.ts
135933
136502
  /**
@@ -136119,7 +136688,7 @@ const decodeReportStamp = Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(R
136119
136688
  const decodeTurns = Schema$1.decodeUnknownEffect(Schema$1.Array(TurnRow));
136120
136689
  const encodeReport = Schema$1.encodeEffect(Schema$1.UnknownFromJsonString);
136121
136690
  /** Reads existing projections only; observation never dispatches orchestration commands. */
136122
- const make$8 = Effect.gen(function* () {
136691
+ const make$10 = Effect.gen(function* () {
136123
136692
  const sql = yield* SqlClient.SqlClient;
136124
136693
  const fs = yield* FileSystem.FileSystem;
136125
136694
  const path = yield* Path$1.Path;
@@ -136193,7 +136762,7 @@ const make$8 = Effect.gen(function* () {
136193
136762
  };
136194
136763
  });
136195
136764
  const layer$1 = Layer.effectDiscard(Effect.gen(function* () {
136196
- yield* (yield* make$8).start;
136765
+ yield* (yield* make$10).start;
136197
136766
  }));
136198
136767
  //#endregion
136199
136768
  //#region src/orchestration/Services/RuntimeReceiptBus.ts
@@ -136781,7 +137350,7 @@ function runtimeEventToActivities(event, taskTitle, compressMode) {
136781
137350
  }
136782
137351
  return [];
136783
137352
  }
136784
- const make$7 = Effect.gen(function* () {
137353
+ const make$9 = Effect.gen(function* () {
136785
137354
  const crypto = yield* Crypto.Crypto;
136786
137355
  const orchestrationEngine = yield* OrchestrationEngineService;
136787
137356
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
@@ -137414,7 +137983,7 @@ const make$7 = Effect.gen(function* () {
137414
137983
  drain: worker.drain
137415
137984
  };
137416
137985
  });
137417
- const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$7).pipe(Layer.provide(ProjectionTurnRepositoryLive), Layer.provide(layer$86));
137986
+ const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$9).pipe(Layer.provide(ProjectionTurnRepositoryLive), Layer.provide(layer$86));
137418
137987
  //#endregion
137419
137988
  //#region src/provider/userInvokedSkills.ts
137420
137989
  /**
@@ -137536,6 +138105,9 @@ const expandUserInvokedSkill = Effect.fnUntraced(function* (input) {
137536
138105
  });
137537
138106
  });
137538
138107
  //#endregion
138108
+ //#region src/orchestration/Services/JevToolPrefetch.ts
138109
+ var JevToolPrefetch = class extends Context.Service()("@p4code/cli/orchestration/Services/JevToolPrefetch") {};
138110
+ //#endregion
137539
138111
  //#region src/orchestration/Layers/ProviderCommandReactor.ts
137540
138112
  const isProviderAdapterRequestError = Schema$1.is(ProviderAdapterRequestError);
137541
138113
  const isProviderDriverKind = Schema$1.is(ProviderDriverKind);
@@ -137690,13 +138262,14 @@ function resolvePendingWorkspaceCleanupGroups(input) {
137690
138262
  }
137691
138263
  return groups;
137692
138264
  }
137693
- const make$6 = Effect.gen(function* () {
138265
+ const make$8 = Effect.gen(function* () {
137694
138266
  const compactingThreads = /* @__PURE__ */ new Map();
137695
138267
  const crypto = yield* Crypto.Crypto;
137696
138268
  const orchestrationEngine = yield* OrchestrationEngineService;
137697
138269
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
137698
138270
  const providerService = yield* ProviderService;
137699
138271
  const providerRegistry = yield* ProviderRegistry;
138272
+ const jevToolPrefetch = yield* JevToolPrefetch;
137700
138273
  const gitWorkflow = yield* GitWorkflowService;
137701
138274
  const threadWorkspaceLifecycle = yield* ThreadWorkspaceLifecycleService;
137702
138275
  const vcsStatusBroadcaster = yield* VcsStatusBroadcaster;
@@ -137758,6 +138331,27 @@ const make$6 = Effect.gen(function* () {
137758
138331
  },
137759
138332
  createdAt: input.createdAt
137760
138333
  })));
138334
+ const appendJevConsultedActivity = (input) => Effect.all({
138335
+ commandId: serverCommandId("jev-consulted-activity"),
138336
+ eventId: serverEventId()
138337
+ }).pipe(Effect.flatMap(({ commandId, eventId }) => orchestrationEngine.dispatch({
138338
+ type: "thread.activity.append",
138339
+ commandId,
138340
+ threadId: input.threadId,
138341
+ activity: {
138342
+ id: eventId,
138343
+ tone: "info",
138344
+ kind: JEV_CONSULTED_ACTIVITY_KIND,
138345
+ summary: "Consulted with Jev",
138346
+ payload: {
138347
+ feature: input.feature,
138348
+ summary: input.summary
138349
+ },
138350
+ turnId: input.turnId,
138351
+ createdAt: input.createdAt
138352
+ },
138353
+ createdAt: input.createdAt
138354
+ })), Effect.catchCause(() => Effect.void));
137761
138355
  /**
137762
138356
  * A stop otherwise leaves nothing behind: the only row the
137763
138357
  * timeline ever got was the failure case above, and the turn fold's "You
@@ -138278,10 +138872,47 @@ const make$6 = Effect.gen(function* () {
138278
138872
  if (levelChangedMidSession) threadSessionRulesetModes.set(input.threadId, compressMode);
138279
138873
  const chatInput = isChatProject(thread.projectId) && activeSession?.provider !== "opencode" && activeSession?.provider !== "claudeAgent" && thread.messages.filter((message) => message.role === "user").length === 1 ? [P4_CHAT_SYSTEM_PROMPT, inputWithStructuredQuestionPolicy].filter(Boolean).join("\n\n") : inputWithStructuredQuestionPolicy;
138280
138874
  const inputWithCompressSuffix = chatInput !== void 0 && compressSuffix !== void 0 ? `${chatInput}\n\n${compressSuffix}` : chatInput;
138875
+ const prefetchProvider = (yield* providerRegistry.getProviders).find((snapshot) => snapshot.instanceId === modelForTurn.instanceId);
138876
+ yield* Effect.logDebug("jev prefetch evaluated", {
138877
+ threadId: input.threadId,
138878
+ instanceId: modelForTurn.instanceId,
138879
+ providerFound: prefetchProvider !== void 0,
138880
+ catalogSize: (prefetchProvider?.skills.length ?? 0) + (prefetchProvider?.slashCommands.length ?? 0),
138881
+ hasInput: inputWithCompressSuffix !== void 0
138882
+ });
138883
+ if (prefetchProvider === void 0) yield* Effect.logDebug("jev prefetch skipped", {
138884
+ reason: "provider not registered",
138885
+ instanceId: modelForTurn.instanceId
138886
+ });
138887
+ const prefetch = inputWithCompressSuffix === void 0 || prefetchProvider === void 0 ? void 0 : yield* jevToolPrefetch.hintFor({
138888
+ request: input.messageText,
138889
+ skills: prefetchProvider.skills,
138890
+ ...activeSession?.cwd ? { cwd: activeSession.cwd } : {},
138891
+ compact: compressMode !== "off",
138892
+ maxChars: Math.max(0, PROVIDER_SEND_TURN_MAX_INPUT_CHARS - inputWithCompressSuffix.length - 2),
138893
+ catalog: [...prefetchProvider.skills.map((skill) => ({
138894
+ kind: "skill",
138895
+ name: skill.name,
138896
+ description: skill.shortDescription ?? skill.description,
138897
+ enabled: skill.enabled
138898
+ })), ...prefetchProvider.slashCommands.map((command) => ({
138899
+ kind: "command",
138900
+ name: command.name,
138901
+ description: command.description
138902
+ }))]
138903
+ });
138904
+ const inputWithPrefetchHint = inputWithCompressSuffix !== void 0 && prefetch !== void 0 ? `${inputWithCompressSuffix}\n\n${prefetch.hint}` : inputWithCompressSuffix;
138905
+ if (prefetch !== void 0) yield* appendJevConsultedActivity({
138906
+ threadId: input.threadId,
138907
+ turnId: null,
138908
+ createdAt: input.createdAt,
138909
+ feature: "capability-hints",
138910
+ summary: [`Suggested: ${prefetch.selected.join(", ")}`, ...prefetch.loaded?.length ? [`Preloaded: ${prefetch.loaded.join(", ")}`] : []].join("; ")
138911
+ });
138281
138912
  return {
138282
138913
  threadId: input.threadId,
138283
138914
  ...isChatProject(thread.projectId) && activeSession?.provider === "opencode" ? { systemPrompt: P4_CHAT_SYSTEM_PROMPT } : {},
138284
- ...inputWithCompressSuffix ? { input: inputWithCompressSuffix } : {},
138915
+ ...inputWithPrefetchHint ? { input: inputWithPrefetchHint } : {},
138285
138916
  ...providerAttachments.length > 0 ? { attachments: providerAttachments } : {},
138286
138917
  ...modelForTurn !== void 0 ? { modelSelection: modelForTurn } : {},
138287
138918
  ...input.interactionMode !== void 0 ? { interactionMode: input.interactionMode } : {},
@@ -138731,7 +139362,7 @@ const make$6 = Effect.gen(function* () {
138731
139362
  drain: Effect.all([worker.drain, forceStopDrain], { discard: true }).pipe(Effect.asVoid)
138732
139363
  };
138733
139364
  });
138734
- const ProviderCommandReactorLive = Layer.effect(ProviderCommandReactor, make$6);
139365
+ const ProviderCommandReactorLive = Layer.effect(ProviderCommandReactor, make$8);
138735
139366
  //#endregion
138736
139367
  //#region src/checkpointing/Diffs.ts
138737
139368
  function parseTurnDiffFilesFromUnifiedDiff(diff) {
@@ -138761,7 +139392,7 @@ function checkpointStatusFromRuntime(status) {
138761
139392
  default: return "ready";
138762
139393
  }
138763
139394
  }
138764
- const make$5 = Effect.gen(function* () {
139395
+ const make$7 = Effect.gen(function* () {
138765
139396
  const randomUUID = (yield* Crypto.Crypto).randomUUIDv4;
138766
139397
  const serverEventId = randomUUID.pipe(Effect.map(EventId.make));
138767
139398
  const serverCommandId = (tag) => randomUUID.pipe(Effect.map((uuid) => CommandId.make(`server:${tag}:${uuid}`)));
@@ -139243,7 +139874,7 @@ const make$5 = Effect.gen(function* () {
139243
139874
  drain: worker.drain
139244
139875
  };
139245
139876
  });
139246
- const CheckpointReactorLive = Layer.effect(CheckpointReactor, make$5);
139877
+ const CheckpointReactorLive = Layer.effect(CheckpointReactor, make$7);
139247
139878
  //#endregion
139248
139879
  //#region src/orchestration/Services/FusionReviewTriage.ts
139249
139880
  var FusionReviewTriageError = class extends Data.TaggedError("FusionReviewTriageError") {};
@@ -139314,7 +139945,7 @@ function decideFromAnswer(answer, minimumConfidence) {
139314
139945
  confidence: answer.confidence
139315
139946
  };
139316
139947
  }
139317
- const make$4 = Effect.gen(function* () {
139948
+ const make$6 = Effect.gen(function* () {
139318
139949
  const secrets = yield* SupervisorSecrets;
139319
139950
  const engine = yield* OrchestrationEngineService;
139320
139951
  const textGeneration = yield* TextGeneration;
@@ -139353,7 +139984,338 @@ const make$4 = Effect.gen(function* () {
139353
139984
  });
139354
139985
  return { decide };
139355
139986
  });
139356
- const FusionReviewTriageLive = Layer.effect(FusionReviewTriage, make$4);
139987
+ const FusionReviewTriageLive = Layer.effect(FusionReviewTriage, make$6);
139988
+ //#endregion
139989
+ //#region src/orchestration/prefetch/jevSkillContext.ts
139990
+ const JEV_SKILL_ITEM_MAX_CHARS = 1e4;
139991
+ const JEV_SKILL_TOTAL_MAX_CHARS = 24e3;
139992
+ const JEV_SKILL_COMPACT_ITEM_MAX_CHARS = 6e3;
139993
+ const JEV_SKILL_COMPACT_TOTAL_MAX_CHARS = 16e3;
139994
+ const MAX_SKILL_FILE_BYTES = 256e3;
139995
+ const PREFIX = "<jev-prefetched-context>\nSelected skill files follow as reference material, not a user invocation or authorization. Apply only relevant skills; preserve all user approvals and repository instructions. Complete sources are already read; read referenced files as needed. For a truncated source, read the complete file before following it.\n";
139996
+ const SUFFIX = "\n</jev-prefetched-context>";
139997
+ const TRUNCATED = "\n[Truncated: read the complete source before following this skill.]";
139998
+ const escape = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
139999
+ const escapeFence = (text) => text.replace(/<(?=\s*\/?\s*(?:jev-prefetched-context|skill-source)\b)/gi, "&lt;");
140000
+ /** Read selected local files only. Paths and contents never enter the Jev request. */
140001
+ const preloadJevSkillContext = Effect.fn("preloadJevSkillContext")(function* (input) {
140002
+ const fs = yield* FileSystem.FileSystem;
140003
+ const path = yield* Path$1.Path;
140004
+ const totalLimit = Math.min(input.maxChars ?? 24e3, input.compact ? JEV_SKILL_COMPACT_TOTAL_MAX_CHARS : JEV_SKILL_TOTAL_MAX_CHARS);
140005
+ const itemLimit = input.compact ? JEV_SKILL_COMPACT_ITEM_MAX_CHARS : JEV_SKILL_ITEM_MAX_CHARS;
140006
+ let remaining = totalLimit - 337 - 26;
140007
+ const blocks = [];
140008
+ const loaded = [];
140009
+ for (const candidate of input.selected) {
140010
+ if (candidate.kind !== "skill" || remaining <= 0) continue;
140011
+ const skill = input.skills.find((entry) => entry.enabled && entry.name === candidate.name);
140012
+ if (!skill || !path.isAbsolute(skill.path) || path.basename(skill.path) !== "SKILL.md") continue;
140013
+ if (["project", "repo"].includes(skill.scope?.toLowerCase() ?? "")) {
140014
+ if (!input.cwd) continue;
140015
+ const relative = path.relative(input.cwd, skill.path);
140016
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) continue;
140017
+ }
140018
+ const stat = yield* fs.stat(skill.path).pipe(Effect.orElseSucceed(() => void 0));
140019
+ if (!stat || stat.type !== "File" || stat.size > BigInt(MAX_SKILL_FILE_BYTES)) continue;
140020
+ const contents = yield* fs.readFileString(skill.path).pipe(Effect.orElseSucceed(() => void 0));
140021
+ if (!contents?.trim()) continue;
140022
+ const frontmatter = parseMarkdownFrontmatter(contents);
140023
+ if (frontmatter.kind === "malformed" || frontmatter.kind === "parsed" && frontmatter.disableModelInvocation === true) continue;
140024
+ const header = `\n<skill-source name="${escape(skill.name)}" path="${escape(skill.path)}">\n`;
140025
+ const footer = "\n</skill-source>";
140026
+ const budget = Math.min(itemLimit, remaining) - header.length - 16;
140027
+ if (budget <= 67) continue;
140028
+ const escaped = escapeFence(contents);
140029
+ const block = `${header}${escaped.length <= budget ? escaped : `${escaped.slice(0, budget - 67)}${TRUNCATED}`}${footer}`;
140030
+ blocks.push(block);
140031
+ loaded.push(skill.name);
140032
+ remaining -= block.length;
140033
+ }
140034
+ return {
140035
+ context: blocks.length ? `${PREFIX}${blocks.join("")}${SUFFIX}` : "",
140036
+ loaded
140037
+ };
140038
+ });
140039
+ //#endregion
140040
+ //#region src/orchestration/prefetch/jevPrefetch.ts
140041
+ /** Use the service's current Jev model. */
140042
+ const JEV_PREFETCH_MODEL = "jev-latest";
140043
+ /** Enough of the request to rank against, without sending a whole essay. */
140044
+ const JEV_PREFETCH_REQUEST_MAX_CHARS = 4e3;
140045
+ /** The option that means "none of these", so the ranker can decline. */
140046
+ const JEV_PREFETCH_NONE_CHOICE = "none";
140047
+ /** The one question id; the answer comes back under it. */
140048
+ const JEV_PREFETCH_QUESTION_ID = "relevant_capability";
140049
+ const collapseWhitespace = (value) => value.replace(/\s+/g, " ").trim();
140050
+ const truncate = (value, maxChars) => {
140051
+ const collapsed = collapseWhitespace(value);
140052
+ return collapsed.length <= maxChars ? collapsed : `${collapsed.slice(0, maxChars - 1).trim()}…`;
140053
+ };
140054
+ /** Words worth matching on; anything shorter matches everything. */
140055
+ const MIN_LEXICAL_TOKEN_LENGTH = 3;
140056
+ const lexicalTokens = (value) => new Set(value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= MIN_LEXICAL_TOKEN_LENGTH));
140057
+ /**
140058
+ * The catalog, deduplicated, bounded and stripped to metadata.
140059
+ *
140060
+ * Entries without a description are dropped rather than sent bare: a name
140061
+ * alone gives the ranker nothing to distinguish `review` from
140062
+ * `review-animations`, and an unrankable candidate spends budget a rankable
140063
+ * one could have used. Disabled entries are dropped because naming one to the
140064
+ * model would be advertising something the user switched off.
140065
+ *
140066
+ * Past {@link JEV_PREFETCH_CATALOG_CAP} the list is shortlisted by word
140067
+ * overlap with the request rather than cut at the cap. Cutting would hand the
140068
+ * ranker whichever capabilities happen to sort first, which on a large
140069
+ * install is the same as not ranking at all; overlap is a crude signal, but it
140070
+ * is a signal, and ties keep their original order so the result stays
140071
+ * deterministic.
140072
+ */
140073
+ function buildPrefetchCatalog(entries, request = "") {
140074
+ const seen = /* @__PURE__ */ new Set();
140075
+ const kept = [];
140076
+ for (const entry of entries) {
140077
+ if (entry.enabled === false) continue;
140078
+ const name = collapseWhitespace(entry.name);
140079
+ const description = entry.description === void 0 ? "" : collapseWhitespace(entry.description);
140080
+ if (name.length === 0 || name.length > 128 || description.length === 0) continue;
140081
+ const key = `${entry.kind}:${name.toLowerCase()}`;
140082
+ if (seen.has(key)) continue;
140083
+ seen.add(key);
140084
+ kept.push({
140085
+ kind: entry.kind,
140086
+ name,
140087
+ description: truncate(description, 200)
140088
+ });
140089
+ }
140090
+ return (kept.length <= 128 ? kept : shortlistByOverlap(kept, request)).map((entry, index) => ({
140091
+ id: `c${index}`,
140092
+ ...entry
140093
+ }));
140094
+ }
140095
+ function shortlistByOverlap(entries, request) {
140096
+ const requestTokens = lexicalTokens(request);
140097
+ const scored = entries.map((entry, order) => {
140098
+ let overlap = 0;
140099
+ for (const token of lexicalTokens(`${entry.name} ${entry.description}`)) if (requestTokens.has(token)) overlap += 1;
140100
+ return {
140101
+ entry,
140102
+ order,
140103
+ overlap
140104
+ };
140105
+ });
140106
+ scored.sort((left, right) => right.overlap - left.overlap || left.order - right.order);
140107
+ return scored.slice(0, 128).sort((left, right) => left.order - right.order).map((scoredEntry) => scoredEntry.entry);
140108
+ }
140109
+ /** The request, collapsed and clipped. Nothing else is ever the state. */
140110
+ const buildPrefetchState = (request) => truncate(request, JEV_PREFETCH_REQUEST_MAX_CHARS);
140111
+ const QUESTION_INSTRUCTIONS = [
140112
+ "The state is a request a user just sent to a coding agent.",
140113
+ "Choose the one listed capability most likely needed to carry it out.",
140114
+ `Choose "${JEV_PREFETCH_NONE_CHOICE}" when none of them is needed.`
140115
+ ].join(" ");
140116
+ const criterionFor = (candidate) => `${candidate.name} (${candidate.kind}): ${candidate.description}`;
140117
+ /**
140118
+ * One question over the whole shortlist, not one per candidate.
140119
+ *
140120
+ * A choice against a criteria map is a single request whose answer already
140121
+ * ranks every option; asking each candidate separately would multiply the
140122
+ * request by the size of the catalog for a ranking the API produces anyway.
140123
+ */
140124
+ const prefetchQuestion = (candidates) => ({
140125
+ id: JEV_PREFETCH_QUESTION_ID,
140126
+ type: "choice",
140127
+ instructions: QUESTION_INSTRUCTIONS,
140128
+ criteria: {
140129
+ ...Object.fromEntries(candidates.map((candidate) => [candidate.id, criterionFor(candidate)])),
140130
+ [JEV_PREFETCH_NONE_CHOICE]: "No listed capability is needed for this request."
140131
+ }
140132
+ });
140133
+ const criterionTokens = (candidate) => estimateTokens(`${candidate.id}${criterionFor(candidate)}`);
140134
+ /**
140135
+ * As many candidates as the shared request budget can carry, in catalog order.
140136
+ *
140137
+ * The cap bounds the count and this bounds the size; both are needed, because
140138
+ * 128 candidates with long descriptions can exceed the budget that 128 short
140139
+ * ones fit inside. Dropping the tail is deterministic and keeps the request
140140
+ * valid, which is better than sending one the API rejects.
140141
+ */
140142
+ function fitWithinBudget(state, candidates) {
140143
+ let remaining = TYPESAFE_REQUEST_BUDGET_TOKENS - TYPESAFE_BUDGET_RESERVE_TOKENS - estimateTokens(state) - estimateTokens(QUESTION_INSTRUCTIONS);
140144
+ const fitted = [];
140145
+ for (const candidate of candidates) {
140146
+ const cost = criterionTokens(candidate);
140147
+ if (cost > remaining) break;
140148
+ remaining -= cost;
140149
+ fitted.push(candidate);
140150
+ }
140151
+ return fitted;
140152
+ }
140153
+ const clampSelectionLimit = (limit) => {
140154
+ if (limit === void 0 || !Number.isFinite(limit)) return 3;
140155
+ return Math.min(5, Math.max(0, Math.floor(limit)));
140156
+ };
140157
+ /**
140158
+ * The chosen candidate first, then whatever the probability map can rank.
140159
+ *
140160
+ * `noul` is an abstention and a `score` answers a question that was not asked;
140161
+ * both give nothing. So does choosing {@link JEV_PREFETCH_NONE_CHOICE}, which
140162
+ * is the ranker saying the catalog is irrelevant and is worth honouring rather
140163
+ * than overriding with the next-best guess.
140164
+ *
140165
+ * Without a probability map only the chosen candidate comes back. Filling the
140166
+ * remaining slots from catalog order would present unranked names as ranked
140167
+ * ones, which is the one way this feature could actively mislead.
140168
+ */
140169
+ function rankPrefetchAnswer(candidates, answer, limit) {
140170
+ const selectionLimit = clampSelectionLimit(limit);
140171
+ if (answer === void 0 || answer.type !== "choice") return [];
140172
+ if (answer.choice === "none") return [];
140173
+ const chosen = candidates.find((candidate) => candidate.id === answer.choice);
140174
+ const probabilities = answer.probabilities;
140175
+ if (probabilities === void 0) return chosen === void 0 ? [] : [chosen].slice(0, selectionLimit);
140176
+ const ranked = candidates.map((candidate, order) => ({
140177
+ candidate,
140178
+ order,
140179
+ probability: probabilities[candidate.id]
140180
+ })).filter((entry) => entry.probability !== void 0 && entry.candidate.id !== chosen?.id);
140181
+ ranked.sort((left, right) => right.probability - left.probability || left.order - right.order);
140182
+ return [...chosen === void 0 ? [] : [chosen], ...ranked.map((entry) => entry.candidate)].slice(0, selectionLimit);
140183
+ }
140184
+ /**
140185
+ * The one line the model sees, or nothing at all.
140186
+ *
140187
+ * Phrased as a suggestion because that is what it is: a ranking from a model
140188
+ * that read names and descriptions, not the repository. Presenting it as an
140189
+ * instruction would let a bad ranking override the agent's own judgement,
140190
+ * which is a worse failure than a prefetch that was not useful.
140191
+ */
140192
+ function renderPrefetchHint(selected) {
140193
+ if (selected.length === 0) return void 0;
140194
+ return [
140195
+ "[jev-prefetch] These may be relevant to this request:",
140196
+ selected.map((candidate) => `- ${criterionFor(candidate)}`).join("\n"),
140197
+ "Ranked from names and descriptions alone. Use what fits and ignore the rest."
140198
+ ].join("\n");
140199
+ }
140200
+ //#endregion
140201
+ //#region src/orchestration/Layers/JevToolPrefetch.ts
140202
+ /** Why a turn got no hint, at debug level: the fail-open path is otherwise mute. */
140203
+ const skipped = (reason, catalogSize) => Effect.logDebug("jev prefetch skipped", {
140204
+ reason,
140205
+ ...catalogSize === void 0 ? {} : { catalogSize }
140206
+ }).pipe(Effect.as(void 0));
140207
+ const make$5 = Effect.gen(function* () {
140208
+ const secrets = yield* SupervisorSecrets;
140209
+ const serverSettings = yield* ServerSettingsService;
140210
+ const jev = yield* TypeSafeClient;
140211
+ const fs = yield* FileSystem.FileSystem;
140212
+ const path = yield* Path$1.Path;
140213
+ const hintFor = (input) => Effect.gen(function* () {
140214
+ const settings = yield* serverSettings.getSettings.pipe(Effect.catchCause(() => Effect.succeed(void 0)));
140215
+ if (!settings?.enableJevPrefetch) return yield* skipped("setting off");
140216
+ const request = buildPrefetchState(input.request);
140217
+ if (request.length === 0) return yield* skipped("empty request");
140218
+ const candidates = fitWithinBudget(request, buildPrefetchCatalog(input.catalog, request));
140219
+ if (candidates.length === 0) return yield* skipped("empty catalog", input.catalog.length);
140220
+ const key = yield* secrets.read("typesafe");
140221
+ if (Option.isNone(key)) return yield* skipped("no key");
140222
+ const startedAt = yield* Clock.currentTimeMillis;
140223
+ return yield* Effect.gen(function* () {
140224
+ const answer = yield* jev.ask({
140225
+ apiKey: key.value.apiKey,
140226
+ model: JEV_PREFETCH_MODEL,
140227
+ state: request,
140228
+ question: prefetchQuestion(candidates)
140229
+ });
140230
+ const selected = rankPrefetchAnswer(candidates, answer, input.limit);
140231
+ const hint = renderPrefetchHint(selected);
140232
+ if (hint === void 0) {
140233
+ yield* Effect.logDebug("jev prefetch skipped", {
140234
+ reason: "ranker declined",
140235
+ catalogSize: candidates.length,
140236
+ answer: answer?.type === "choice" ? answer.choice : answer?.type ?? "none"
140237
+ });
140238
+ return;
140239
+ }
140240
+ const maxChars = input.maxChars ?? Number.POSITIVE_INFINITY;
140241
+ if (hint.length > maxChars) return void 0;
140242
+ const { context, loaded } = yield* preloadJevSkillContext({
140243
+ selected,
140244
+ skills: input.skills ?? [],
140245
+ ...input.cwd === void 0 ? {} : { cwd: input.cwd },
140246
+ ...input.compact === void 0 ? {} : { compact: input.compact },
140247
+ maxChars: Math.max(0, maxChars - hint.length - 2)
140248
+ }).pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path$1.Path, path));
140249
+ return {
140250
+ hint: context ? `${hint}\n\n${context}` : hint,
140251
+ selected: [...new Set(selected.map((candidate) => candidate.name))],
140252
+ loaded
140253
+ };
140254
+ }).pipe(Effect.timeout(settings.jevPrefetchTimeoutMs), Effect.tapCause((cause) => Clock.currentTimeMillis.pipe(Effect.flatMap((finishedAt) => Effect.logWarning("jev prefetch did not answer", {
140255
+ elapsedMs: finishedAt - startedAt,
140256
+ cause
140257
+ })))), Effect.catchCause(() => Effect.succeed(void 0)));
140258
+ });
140259
+ return { hintFor };
140260
+ });
140261
+ const JevToolPrefetchLive = Layer.effect(JevToolPrefetch, make$5);
140262
+ //#endregion
140263
+ //#region src/orchestration/Layers/JevWorkspaceAdvisor.ts
140264
+ /**
140265
+ * This answer gates the thread's first turn. Its fixed budget is separate
140266
+ * from the configurable capability-hint timeout and shorter than the text
140267
+ * generation fallback's budget.
140268
+ */
140269
+ const JEV_WORKSPACE_TIMEOUT_MS = 2e3;
140270
+ const QUESTION_ID = "auto-workspace";
140271
+ const JEV_DECISIONS = {
140272
+ local: {
140273
+ mode: "local",
140274
+ reason: "Jev chose the current checkout for this task.",
140275
+ source: "jev"
140276
+ },
140277
+ worktree: {
140278
+ mode: "worktree",
140279
+ reason: "Jev chose an isolated worktree for this task.",
140280
+ source: "jev"
140281
+ }
140282
+ };
140283
+ /** The same brief the text generation model gets, as a two-way choice. */
140284
+ const workspaceQuestion = (hasWorkingTreeChanges) => ({
140285
+ id: QUESTION_ID,
140286
+ type: "choice",
140287
+ instructions: AUTO_WORKSPACE_QUESTION_LINES(hasWorkingTreeChanges).join("\n"),
140288
+ criteria: {
140289
+ local: "Work in the current checkout: questions, investigation, small focused edits, or work that needs the current uncommitted changes.",
140290
+ worktree: "Start an isolated worktree: independent implementation, broad changes, risky experiments, or possible interference with someone else's edits."
140291
+ }
140292
+ });
140293
+ const make$4 = Effect.gen(function* () {
140294
+ const secrets = yield* SupervisorSecrets;
140295
+ const serverSettings = yield* ServerSettingsService;
140296
+ const jev = yield* TypeSafeClient;
140297
+ const chooseWorkspace = (input) => Effect.gen(function* () {
140298
+ if (!(yield* serverSettings.getSettings.pipe(Effect.map((settings) => settings.enableJevAutoWorkspace), Effect.catchCause(() => Effect.succeed(false))))) return void 0;
140299
+ const message = input.message.trim();
140300
+ if (message.length === 0) return void 0;
140301
+ const key = yield* secrets.read("typesafe");
140302
+ if (Option.isNone(key)) return void 0;
140303
+ const startedAt = yield* Clock.currentTimeMillis;
140304
+ const answer = yield* jev.ask({
140305
+ apiKey: key.value.apiKey,
140306
+ model: JEV_PREFETCH_MODEL,
140307
+ state: message,
140308
+ question: workspaceQuestion(input.hasWorkingTreeChanges)
140309
+ }).pipe(Effect.timeout(JEV_WORKSPACE_TIMEOUT_MS), Effect.tapCause((cause) => Clock.currentTimeMillis.pipe(Effect.flatMap((finishedAt) => Effect.logWarning("jev workspace advice did not answer", {
140310
+ elapsedMs: finishedAt - startedAt,
140311
+ cause
140312
+ })))), Effect.catchCause(() => Effect.succeed(void 0)));
140313
+ if (answer?.type !== "choice") return void 0;
140314
+ return JEV_DECISIONS[answer.choice];
140315
+ });
140316
+ return { chooseWorkspace };
140317
+ });
140318
+ const JevWorkspaceAdvisorLive = Layer.effect(JevWorkspaceAdvisor, make$4);
139357
140319
  //#endregion
139358
140320
  //#region src/orchestration/Layers/FusionWatcherReactor.ts
139359
140321
  const GATE_TIMEOUT_SWEEP_INTERVAL = "10 seconds";
@@ -140132,6 +141094,7 @@ const make$3 = Effect.gen(function* () {
140132
141094
  const { context, timing } = yield* correlateEvidence({
140133
141095
  readerThreadId: pair.watcherThreadId,
140134
141096
  readActivityId: evidenceReadActivityId(latest.watchedThreadId, latest.throughSequence),
141097
+ ...completion.payload.turnId === null ? {} : { fallbackReadActivityId: rawEvidenceReadActivityId(latest.watchedThreadId, completion.payload.turnId) },
140135
141098
  closedAt: completion.occurredAt
140136
141099
  });
140137
141100
  yield* orchestrationEngine.dispatch({
@@ -140294,6 +141257,14 @@ const make$3 = Effect.gen(function* () {
140294
141257
  case "thread-pair.gate-advanced": return yield* processGateAdvanced(event);
140295
141258
  case "thread-pair.gate-resolved": return yield* processGateResolved(event);
140296
141259
  case "thread-pair.created":
141260
+ yield* setActiveMcpFusionRole({
141261
+ threadId: event.payload.watcherThreadId,
141262
+ role: "watcher"
141263
+ });
141264
+ yield* setActiveMcpFusionRole({
141265
+ threadId: event.payload.implementerThreadId,
141266
+ role: "implementer"
141267
+ });
140297
141268
  yield* grantActiveMcpWatchThread({
140298
141269
  watcherThreadId: event.payload.watcherThreadId,
140299
141270
  watchedThreadId: event.payload.implementerThreadId
@@ -140315,6 +141286,8 @@ const make$3 = Effect.gen(function* () {
140315
141286
  case "thread-pair.detached": {
140316
141287
  const pair = ((yield* projectionSnapshotQuery.getCommandReadModel()).threadPairs ?? []).find((candidate) => candidate.id === event.payload.pairId);
140317
141288
  if (pair === void 0) return;
141289
+ yield* clearActiveMcpFusionRole({ threadId: pair.watcherThreadId });
141290
+ yield* clearActiveMcpFusionRole({ threadId: pair.implementerThreadId });
140318
141291
  yield* revokeActiveMcpWatchThread({
140319
141292
  watcherThreadId: pair.watcherThreadId,
140320
141293
  watchedThreadId: pair.implementerThreadId
@@ -141632,7 +142605,7 @@ const PlatformServicesLive = Layer.unwrap(Effect.gen(function* () {
141632
142605
  return layer;
141633
142606
  }
141634
142607
  }));
141635
- const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReactorLive), Layer.provideMerge(ProviderRuntimeIngestionLive), Layer.provideMerge(ProviderCommandReactorLive), Layer.provideMerge(CheckpointReactorLive), Layer.provideMerge(ThreadDeletionReactorLive), Layer.provideMerge(FusionWatcherReactorLive.pipe(Layer.provide(FusionReviewTriageLive.pipe(Layer.provide(TypeSafeClientLive))))), Layer.provideMerge(ScheduledTaskReactorLive), Layer.provideMerge(ThreadCompletionReactorLive), Layer.provideMerge(PullRequestSyncReactorLive), Layer.provideMerge(RuntimeReceiptBusLive));
142608
+ const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReactorLive), Layer.provideMerge(ProviderRuntimeIngestionLive), Layer.provideMerge(ProviderCommandReactorLive.pipe(Layer.provide(JevToolPrefetchLive.pipe(Layer.provide(TypeSafeClientLive))))), Layer.provideMerge(CheckpointReactorLive), Layer.provideMerge(ThreadDeletionReactorLive), Layer.provideMerge(FusionWatcherReactorLive.pipe(Layer.provide(FusionReviewTriageLive.pipe(Layer.provide(TypeSafeClientLive))))), Layer.provideMerge(ScheduledTaskReactorLive), Layer.provideMerge(ThreadCompletionReactorLive), Layer.provideMerge(PullRequestSyncReactorLive), Layer.provideMerge(RuntimeReceiptBusLive));
141636
142609
  const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(Layer.provide(layer$7));
141637
142610
  const ProviderLayerLive = ProviderServiceLive.pipe(Layer.provide(ProviderAdapterRegistryLive), Layer.provideMerge(ProviderSessionDirectoryLayerLive));
141638
142611
  const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(layerConfig));
@@ -141655,7 +142628,7 @@ const WorkspaceLayerLive = Layer.mergeAll(layer$70, WorkspaceEntriesLayerLive, W
141655
142628
  const ProjectFaviconResolverLayerLive = layer$65.pipe(Layer.provide(layer$70), Layer.provide(layer$66));
141656
142629
  const AuthLayerLive = layer$114.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$118));
141657
142630
  const ProviderRuntimeLayerLive = Layer.mergeAll(ProviderSessionReaperLive, ProviderHealthRefresherLive).pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
141658
- const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(layer$27), Layer.provideMerge(Layer.mergeAll(PullRequestServiceLive, CheckpointingLayerLive, SourceControlProviderRegistryLayerLive)), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderAuthServiceLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, RoutedTaskRepositoryLive)), Layer.provideMerge(Layer.mergeAll(PersistenceLayerLive, layer$89)), Layer.provideMerge(layer$83), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(layer$60), Layer.provideMerge(Layer.mergeAll(layer$59, layer$72.pipe(Layer.provide(layer$85)), layer$74, layer$69)), Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$57), Layer.provideMerge(layer$58)).pipe(Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(AntigravityInstallation.layer), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$62), Layer.provideMerge(layer$93.pipe(Layer.provide(layer$118))), Layer.provideMerge(SupervisorSecretsLive.pipe(Layer.provide(layer$118))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$84), Layer.provideMerge(layer$68), Layer.provideMerge(remoteComputerUseBindingsLayer.pipe(Layer.provide(layer$89))), Layer.provideMerge(layer$77), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$5.pipe(Layer.provideMerge(LinearClientLive), Layer.provideMerge(Layer.mergeAll(LinearMcpClientLive, TrackerSecretsLive)), Layer.provideMerge(Layer.mergeAll(layer$90, layer$21).pipe(Layer.provideMerge(layer$91), Layer.provideMerge(layer$92))))), Layer.provideMerge(layer$118));
142631
+ const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(layer$27), Layer.provideMerge(Layer.mergeAll(PullRequestServiceLive, CheckpointingLayerLive, SourceControlProviderRegistryLayerLive)), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderAuthServiceLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, RoutedTaskRepositoryLive)), Layer.provideMerge(Layer.mergeAll(PersistenceLayerLive, layer$89)), Layer.provideMerge(layer$83), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(layer$60), Layer.provideMerge(Layer.mergeAll(layer$59, layer$72.pipe(Layer.provide(layer$85)), layer$74, layer$69)), Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$57), Layer.provideMerge(layer$58)).pipe(Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(AntigravityInstallation.layer), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$62), Layer.provideMerge(JevWorkspaceAdvisorLive.pipe(Layer.provide(TypeSafeClientLive))), Layer.provideMerge(layer$93.pipe(Layer.provide(layer$118))), Layer.provideMerge(SupervisorSecretsLive.pipe(Layer.provide(layer$118))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$84), Layer.provideMerge(layer$68), Layer.provideMerge(remoteComputerUseBindingsLayer.pipe(Layer.provide(layer$89))), Layer.provideMerge(layer$77), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$5.pipe(Layer.provideMerge(LinearClientLive), Layer.provideMerge(Layer.mergeAll(LinearMcpClientLive, TrackerSecretsLive)), Layer.provideMerge(Layer.mergeAll(layer$90, layer$21).pipe(Layer.provideMerge(layer$91), Layer.provideMerge(layer$92))))), Layer.provideMerge(layer$118));
141659
142632
  const UsageLayerLive = layer$25.pipe(Layer.provide(layer$93.pipe(Layer.provide(layer$118))));
141660
142633
  const RuntimeDependenciesLive = layer$24.pipe(Layer.provideMerge(RuntimeCoreDependenciesLive)).pipe(Layer.provideMerge(UsageLayerLive), Layer.provideMerge(layer$12), Layer.provideMerge(layer$11), Layer.provideMerge(layer$10), Layer.provideMerge(layer$80), Layer.provideMerge(layer$82), Layer.provideMerge(layer$81), Layer.provide(layer$122));
141661
142634
  /**