@p4code/cli 0.5.4 → 0.5.6

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
@@ -19,8 +19,11 @@ import "effect/LogLevel";
19
19
  import * as Path$1 from "effect/Path";
20
20
  import * as Schema$1 from "effect/Schema";
21
21
  import * as NodePath from "node:path";
22
+ import { isAbsolute } from "node:path";
22
23
  import * as NodeChildProcess from "node:child_process";
24
+ import { spawn } from "node:child_process";
23
25
  import * as NodeFS from "node:fs";
26
+ import { constants } from "node:fs";
24
27
  import * as DateTime from "effect/DateTime";
25
28
  import * as Duration from "effect/Duration";
26
29
  import * as Option from "effect/Option";
@@ -42,6 +45,7 @@ import * as Config from "effect/Config";
42
45
  import * as References from "effect/References";
43
46
  import * as HttpServerRequest$1 from "effect/unstable/http/HttpServerRequest";
44
47
  import * as NodeCrypto from "node:crypto";
48
+ import { createHash, randomUUID } from "node:crypto";
45
49
  import * as Encoding from "effect/Encoding";
46
50
  import * as Result$1 from "effect/Result";
47
51
  import * as Crypto from "effect/Crypto";
@@ -57,6 +61,7 @@ import * as Cause from "effect/Cause";
57
61
  import * as Exit from "effect/Exit";
58
62
  import * as NodeReadline from "node:readline";
59
63
  import * as NodeFSP from "node:fs/promises";
64
+ import { access } from "node:fs/promises";
60
65
  import * as Clock from "effect/Clock";
61
66
  import * as NodeStream from "@effect/platform-node/NodeStream";
62
67
  import * as NodeHttp from "node:http";
@@ -148,7 +153,7 @@ const closeServer = (server) => {
148
153
  * NetService - Service tag for startup networking helpers.
149
154
  */
150
155
  var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
151
- const make$115 = () => {
156
+ const make$116 = () => {
152
157
  /**
153
158
  * Returns true when a TCP server can bind to {host, port}.
154
159
  * `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
@@ -257,10 +262,10 @@ const make$115 = () => {
257
262
  })
258
263
  };
259
264
  };
260
- const layer$118 = Layer.sync(NetService, make$115);
265
+ const layer$119 = Layer.sync(NetService, make$116);
261
266
  //#endregion
262
267
  //#region package.json
263
- var version$1 = "0.5.4";
268
+ var version$1 = "0.5.6";
264
269
  //#endregion
265
270
  //#region src/config.ts
266
271
  /**
@@ -281,8 +286,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
281
286
  /** @deprecated Import and use `layerTest` from this module. */
282
287
  static layerTest = (cwd, baseDirOrPrefix) => layerTest$4(cwd, baseDirOrPrefix);
283
288
  };
284
- const make$114 = (config) => ServerConfig$1.of(config);
285
- const layer$117 = (config) => Layer.succeed(ServerConfig$1, make$114(config));
289
+ const make$115 = (config) => ServerConfig$1.of(config);
290
+ const layer$118 = (config) => Layer.succeed(ServerConfig$1, make$115(config));
286
291
  const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
287
292
  const { join } = yield* Path$1.Path;
288
293
  const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
@@ -3719,9 +3724,21 @@ const OrchestrationThreadPairGate = Schema$1.Struct({
3719
3724
  awaitingWatcherSince: Schema$1.NullOr(IsoDateTime),
3720
3725
  updatedAt: IsoDateTime
3721
3726
  });
3727
+ const FusionReviewPolicy = Schema$1.Struct({
3728
+ mode: Schema$1.Literals(["phase", "risk-and-final"]),
3729
+ maxAutomaticReviews: PositiveInt.pipe(Schema$1.withDecodingDefault(Effect.succeed(4))),
3730
+ maxCorrectionRounds: NonNegativeInt.pipe(Schema$1.withDecodingDefault(Effect.succeed(2)))
3731
+ });
3732
+ const FusionReviewExperiment = Schema$1.Struct({
3733
+ policy: FusionReviewPolicy,
3734
+ lastMessageSequence: Schema$1.optionalKey(NonNegativeInt),
3735
+ automaticReviews: NonNegativeInt,
3736
+ correctionRounds: NonNegativeInt
3737
+ });
3722
3738
  /** Persisted relationship between two otherwise ordinary threads. */
3723
3739
  const OrchestrationThreadPair = Schema$1.Struct({
3724
3740
  id: ThreadPairId,
3741
+ reviewExperiment: Schema$1.optionalKey(Schema$1.NullOr(FusionReviewExperiment)),
3725
3742
  implementerThreadId: ThreadId,
3726
3743
  watcherThreadId: ThreadId,
3727
3744
  lastReviewedImplementerSequence: NonNegativeInt,
@@ -4245,6 +4262,7 @@ const ThreadPairCreateCommand = Schema$1.Struct({
4245
4262
  implementerThreadId: ThreadId,
4246
4263
  watcherThreadId: ThreadId,
4247
4264
  continuationMessageId: Schema$1.optional(MessageId),
4265
+ reviewPolicy: Schema$1.optionalKey(Schema$1.NullOr(FusionReviewPolicy)),
4248
4266
  roundCap: Schema$1.optional(NonNegativeInt),
4249
4267
  gateTimeoutMs: Schema$1.optional(NonNegativeInt),
4250
4268
  createdAt: IsoDateTime
@@ -4770,6 +4788,7 @@ const ThreadPairCreatedPayload$1 = Schema$1.Struct({
4770
4788
  lastReviewedImplementerSequence: NonNegativeInt,
4771
4789
  activationTurnId: Schema$1.optional(Schema$1.NullOr(TurnId)),
4772
4790
  continuationMessageId: Schema$1.optional(MessageId),
4791
+ reviewPolicy: Schema$1.optionalKey(Schema$1.NullOr(FusionReviewPolicy)),
4773
4792
  roundCap: Schema$1.optional(NonNegativeInt),
4774
4793
  gateTimeoutMs: Schema$1.optional(NonNegativeInt),
4775
4794
  createdAt: IsoDateTime
@@ -7809,6 +7828,7 @@ const TaskProgressPayload = Schema$1.Struct({
7809
7828
  usage: Schema$1.optional(Schema$1.Unknown),
7810
7829
  lastToolName: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
7811
7830
  toolUseId: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
7831
+ model: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
7812
7832
  subagentType: Schema$1.optional(TrimmedNonEmptyStringSchema$1)
7813
7833
  });
7814
7834
  const TaskCompletedPayload = Schema$1.Struct({
@@ -8824,7 +8844,11 @@ const DeviceHostSummary = Schema$1.Struct({
8824
8844
  label: TrimmedNonEmptyString.check(Schema$1.isMaxLength(MAX_HOST_LABEL_LENGTH)),
8825
8845
  platforms: Schema$1.Array(DevicePlatformAvailability),
8826
8846
  hubInstalled: Schema$1.Boolean,
8827
- agentDeviceInstalled: Schema$1.Boolean
8847
+ agentDeviceInstalled: Schema$1.Boolean,
8848
+ automation: Schema$1.optional(Schema$1.Struct({
8849
+ provider: Schema$1.Literals(["apple-xcode", "xcodebuildmcp"]),
8850
+ detail: Schema$1.String
8851
+ }))
8828
8852
  });
8829
8853
  /**
8830
8854
  * Lifecycle of the helper processes on a host. Tools are installed on first
@@ -10153,6 +10177,10 @@ const ServerSettings = Schema$1.Struct({
10153
10177
  * smaller; off is the rollback if a pair misbehaves.
10154
10178
  */
10155
10179
  enableOptimizedFusionPromptDelivery: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
10180
+ /** Local daily measurements and weekly recommendations; never starts agent turns. */
10181
+ enableFusionObservation: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
10182
+ /** Experimental policy captured by new pairs; null preserves existing behavior. */
10183
+ fusionReviewPolicy: Schema$1.NullOr(FusionReviewPolicy).pipe(Schema$1.withDecodingDefault(Effect.succeed(null))),
10156
10184
  /**
10157
10185
  * Whether the board offers the scoping agent: the Scope action on a task and
10158
10186
  * the panel that drafts a new one by interview.
@@ -10359,6 +10387,8 @@ const ServerSettingsPatch = Schema$1.Struct({
10359
10387
  enableRootCauseBeforeFix: Schema$1.optionalKey(Schema$1.Boolean),
10360
10388
  enableUnpromptedSubagents: Schema$1.optionalKey(Schema$1.Boolean),
10361
10389
  enableOptimizedFusionPromptDelivery: Schema$1.optionalKey(Schema$1.Boolean),
10390
+ enableFusionObservation: Schema$1.optionalKey(Schema$1.Boolean),
10391
+ fusionReviewPolicy: Schema$1.optionalKey(Schema$1.NullOr(FusionReviewPolicy)),
10362
10392
  enableScopingAgent: Schema$1.optionalKey(Schema$1.Boolean),
10363
10393
  enablePlanPhase: Schema$1.optionalKey(Schema$1.Boolean),
10364
10394
  writeGeneratedSkillsToRepo: Schema$1.optionalKey(Schema$1.Boolean),
@@ -13571,6 +13601,45 @@ const ComputerUseDeviceTarget = {
13571
13601
  deviceId: DeviceId,
13572
13602
  leaseId: DeviceId
13573
13603
  };
13604
+ const DeviceElementRef = TrimmedNonEmptyString.check(Schema$1.isMaxLength(128));
13605
+ const DeviceGesture = {
13606
+ direction: Schema$1.Literals([
13607
+ "up",
13608
+ "down",
13609
+ "left",
13610
+ "right"
13611
+ ]),
13612
+ distance: Schema$1.optional(Schema$1.Number.check(Schema$1.isGreaterThan(0), Schema$1.isLessThanOrEqualTo(1)))
13613
+ };
13614
+ const ComputerUseDeviceAction = Schema$1.Union([
13615
+ Schema$1.Struct({
13616
+ action: Schema$1.Literal("tap"),
13617
+ elementRef: DeviceElementRef
13618
+ }),
13619
+ Schema$1.Struct({
13620
+ action: Schema$1.Literal("type_text"),
13621
+ elementRef: DeviceElementRef,
13622
+ text: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(16e3)),
13623
+ replaceExisting: Schema$1.optional(Schema$1.Boolean)
13624
+ }),
13625
+ Schema$1.Struct({
13626
+ action: Schema$1.Literal("swipe"),
13627
+ withinElementRef: DeviceElementRef,
13628
+ ...DeviceGesture
13629
+ }),
13630
+ Schema$1.Struct({
13631
+ action: Schema$1.Literal("drag"),
13632
+ elementRef: DeviceElementRef,
13633
+ ...DeviceGesture
13634
+ }),
13635
+ Schema$1.Struct({
13636
+ action: Schema$1.Literal("batch"),
13637
+ steps: Schema$1.Array(Schema$1.Struct({
13638
+ action: Schema$1.Literal("tap"),
13639
+ elementRef: DeviceElementRef
13640
+ })).check(Schema$1.isMinLength(1), Schema$1.isMaxLength(100))
13641
+ })
13642
+ ]);
13574
13643
  const ComputerUseDeviceOperation = Schema$1.Union([
13575
13644
  Schema$1.Struct({ method: Schema$1.Literal("list") }),
13576
13645
  Schema$1.Struct({
@@ -13585,7 +13654,18 @@ const ComputerUseDeviceOperation = Schema$1.Union([
13585
13654
  "screenshot",
13586
13655
  "close"
13587
13656
  ]),
13588
- ...ComputerUseDeviceTarget
13657
+ ...ComputerUseDeviceTarget,
13658
+ sinceScreenHash: Schema$1.optional(TrimmedNonEmptyString)
13659
+ }),
13660
+ Schema$1.Struct({
13661
+ method: Schema$1.Literal("semantic"),
13662
+ ...ComputerUseDeviceTarget,
13663
+ action: ComputerUseDeviceAction
13664
+ }),
13665
+ Schema$1.Struct({
13666
+ method: Schema$1.Literal("recording"),
13667
+ ...ComputerUseDeviceTarget,
13668
+ action: Schema$1.Literals(["start", "stop"])
13589
13669
  }),
13590
13670
  Schema$1.Struct({
13591
13671
  method: Schema$1.Literal("command"),
@@ -13697,6 +13777,7 @@ const WS_METHODS = {
13697
13777
  serverGetProcessDiagnostics: "server.getProcessDiagnostics",
13698
13778
  serverGetProcessResourceHistory: "server.getProcessResourceHistory",
13699
13779
  serverSignalProcess: "server.signalProcess",
13780
+ threadHandoff: "thread.handoff",
13700
13781
  btwAsk: "btw.ask",
13701
13782
  btwCancel: "btw.cancel",
13702
13783
  pullRequestsList: "pullRequests.list",
@@ -14340,6 +14421,14 @@ const WsServerGetProviderUsageRpc = Rpc.make(WS_METHODS.serverGetProviderUsage,
14340
14421
  success: Schema$1.Struct({ report: Schema$1.String }),
14341
14422
  error: Schema$1.Union([TextGenerationError, EnvironmentAuthorizationError])
14342
14423
  });
14424
+ const WsThreadHandoffRpc = Rpc.make(WS_METHODS.threadHandoff, {
14425
+ payload: Schema$1.Struct({ threadId: ThreadId }),
14426
+ success: Schema$1.Struct({
14427
+ projectId: ProjectId,
14428
+ text: Schema$1.String
14429
+ }),
14430
+ error: Schema$1.Union([TextGenerationError, EnvironmentAuthorizationError])
14431
+ });
14343
14432
  const WsBtwAskRpc = Rpc.make(WS_METHODS.btwAsk, {
14344
14433
  payload: BtwAskInput,
14345
14434
  success: BtwAskResult,
@@ -14800,7 +14889,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
14800
14889
  error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
14801
14890
  stream: true
14802
14891
  });
14803
- const WsRpcGroup = RpcGroup.make(WsDeviceGetStateRpc, WsDeviceListRpc, WsDeviceConfigureRpc, WsDeviceOpenRpc, WsDeviceCloseRpc, WsDeviceDetailRpc, WsDeviceActionRpc, WsSubscribeDevicesRpc, WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerProviderAuthStartRpc, WsServerProviderAuthCompleteRpc, WsServerProviderAuthCancelRpc, WsServerProviderAuthLogoutRpc, WsServerProviderAuthSubscribeRpc, WsServerProviderInstallStartRpc, WsServerProviderInstallCancelRpc, WsServerProviderInstallRemoveRpc, WsServerProviderInstallSubscribeRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetHostResourcesRpc, WsServerGetResourceTelemetryRpc, WsServerGetResourceTelemetryHistoryRpc, WsServerRetryResourceTelemetryRpc, WsSubscribeResourceTelemetryRpc, WsBtwAskRpc, WsBtwCancelRpc, WsServerGetUsageSummaryRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsPullRequestsListRpc, WsPullRequestsListStatsRpc, WsPullRequestsDetailRpc, WsPullRequestsStackRpc, WsPullRequestsActivityRpc, WsPullRequestsDiffFileContentsRpc, WsPullRequestsRunActionRpc, WsPullRequestsUpdateRpc, WsPullRequestsCommentRpc, WsPullRequestsUpdateCommentRpc, WsPullRequestsSubmitReviewRpc, WsPullRequestsReplyToThreadRpc, WsPullRequestsSetThreadResolutionRpc, WsPullRequestsSetReactionRpc, WsPullRequestsInvalidateRpc, WsPullRequestsReviewerCandidatesRpc, WsPullRequestsRequestReviewersRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchContentsRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsComputerUseConnectRpc, WsComputerUseListRpc, WsComputerUseControlsRpc, WsComputerUseControlRpc, WsComputerUsePreviewRpc, WsComputerUseRespondRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsFeedListRpc, WsFeedRefreshRpc, WsFeedSourcesListRpc, WsFeedSourcesUpsertRpc, WsFeedSourcesDeleteRpc, WsFeedMarkReadRpc, WsFeedCleanupRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsTrackerStatusRpc, WsTrackerSetApiKeyRpc, WsTrackerClearApiKeyRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetWorkflowScriptRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationSearchThreadsRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
14892
+ const WsRpcGroup = RpcGroup.make(WsDeviceGetStateRpc, WsDeviceListRpc, WsDeviceConfigureRpc, WsDeviceOpenRpc, WsDeviceCloseRpc, WsDeviceDetailRpc, WsDeviceActionRpc, WsSubscribeDevicesRpc, WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerProviderAuthStartRpc, WsServerProviderAuthCompleteRpc, WsServerProviderAuthCancelRpc, WsServerProviderAuthLogoutRpc, WsServerProviderAuthSubscribeRpc, WsServerProviderInstallStartRpc, WsServerProviderInstallCancelRpc, WsServerProviderInstallRemoveRpc, WsServerProviderInstallSubscribeRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetHostResourcesRpc, WsServerGetResourceTelemetryRpc, WsServerGetResourceTelemetryHistoryRpc, WsServerRetryResourceTelemetryRpc, WsSubscribeResourceTelemetryRpc, WsThreadHandoffRpc, WsBtwAskRpc, WsBtwCancelRpc, WsServerGetUsageSummaryRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsPullRequestsListRpc, WsPullRequestsListStatsRpc, WsPullRequestsDetailRpc, WsPullRequestsStackRpc, WsPullRequestsActivityRpc, WsPullRequestsDiffFileContentsRpc, WsPullRequestsRunActionRpc, WsPullRequestsUpdateRpc, WsPullRequestsCommentRpc, WsPullRequestsUpdateCommentRpc, WsPullRequestsSubmitReviewRpc, WsPullRequestsReplyToThreadRpc, WsPullRequestsSetThreadResolutionRpc, WsPullRequestsSetReactionRpc, WsPullRequestsInvalidateRpc, WsPullRequestsReviewerCandidatesRpc, WsPullRequestsRequestReviewersRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchContentsRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsComputerUseConnectRpc, WsComputerUseListRpc, WsComputerUseControlsRpc, WsComputerUseControlRpc, WsComputerUsePreviewRpc, WsComputerUseRespondRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsFeedListRpc, WsFeedRefreshRpc, WsFeedSourcesListRpc, WsFeedSourcesUpsertRpc, WsFeedSourcesDeleteRpc, WsFeedMarkReadRpc, WsFeedCleanupRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsTrackerStatusRpc, WsTrackerSetApiKeyRpc, WsTrackerClearApiKeyRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetWorkflowScriptRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationSearchThreadsRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
14804
14893
  //#endregion
14805
14894
  //#region ../../packages/shared/src/oauthScope.ts
14806
14895
  const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
@@ -14946,7 +15035,7 @@ function deriveAuthClientMetadata(input) {
14946
15035
  //#endregion
14947
15036
  //#region src/auth/EnvironmentAuthPolicy.ts
14948
15037
  var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
14949
- const make$113 = Effect.gen(function* () {
15038
+ const make$114 = Effect.gen(function* () {
14950
15039
  const config = yield* ServerConfig$1;
14951
15040
  const isRemoteReachable = isRemoteReachableHost(config.host);
14952
15041
  const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
@@ -14964,7 +15053,7 @@ const make$113 = Effect.gen(function* () {
14964
15053
  };
14965
15054
  return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
14966
15055
  });
14967
- const layer$116 = Layer.effect(EnvironmentAuthPolicy, make$113);
15056
+ const layer$117 = Layer.effect(EnvironmentAuthPolicy, make$114);
14968
15057
  //#endregion
14969
15058
  //#region src/persistence/Errors.ts
14970
15059
  function summarizeSchemaIssue(issue) {
@@ -15145,7 +15234,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
15145
15234
  cause
15146
15235
  });
15147
15236
  }
15148
- const make$112 = Effect.gen(function* () {
15237
+ const make$113 = Effect.gen(function* () {
15149
15238
  const sql = yield* SqlClient.SqlClient;
15150
15239
  const createSessionRow = SqlSchema.void({
15151
15240
  Request: CreateAuthSessionInput,
@@ -15279,7 +15368,7 @@ const make$112 = Effect.gen(function* () {
15279
15368
  setLastConnectedAt
15280
15369
  };
15281
15370
  });
15282
- const layer$115 = Layer.effect(AuthSessionRepository, make$112);
15371
+ const layer$116 = Layer.effect(AuthSessionRepository, make$113);
15283
15372
  //#endregion
15284
15373
  //#region src/auth/ServerSecretStore.ts
15285
15374
  const secretStoreErrorContext = {
@@ -15421,11 +15510,11 @@ const makeForDirectory = Effect.fn("ServerSecretStore.makeForDirectory")(functio
15421
15510
  remove
15422
15511
  });
15423
15512
  });
15424
- const make$111 = Effect.gen(function* () {
15513
+ const make$112 = Effect.gen(function* () {
15425
15514
  const config = yield* ServerConfig$1;
15426
15515
  return yield* makeForDirectory(config.secretsDir);
15427
15516
  });
15428
- const layer$114 = Layer.effect(ServerSecretStore, make$111);
15517
+ const layer$115 = Layer.effect(ServerSecretStore, make$112);
15429
15518
  //#endregion
15430
15519
  //#region src/auth/SessionStore.ts
15431
15520
  var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
@@ -15663,7 +15752,7 @@ function toAuthClientSession(input) {
15663
15752
  current: false
15664
15753
  };
15665
15754
  }
15666
- const make$110 = Effect.gen(function* () {
15755
+ const make$111 = Effect.gen(function* () {
15667
15756
  const crypto = yield* Crypto.Crypto;
15668
15757
  const serverConfig = yield* ServerConfig$1;
15669
15758
  const secretStore = yield* ServerSecretStore;
@@ -15978,7 +16067,7 @@ const make$110 = Effect.gen(function* () {
15978
16067
  markDisconnected
15979
16068
  });
15980
16069
  });
15981
- const layer$113 = Layer.effect(SessionStore, make$110).pipe(Layer.provideMerge(layer$115));
16070
+ const layer$114 = Layer.effect(SessionStore, make$111).pipe(Layer.provideMerge(layer$116));
15982
16071
  //#endregion
15983
16072
  //#region src/persistence/AuthPairingLinks.ts
15984
16073
  const AuthPairingLinkRecord = Schema$1.Struct({
@@ -16039,7 +16128,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
16039
16128
  cause
16040
16129
  });
16041
16130
  }
16042
- const make$109 = Effect.gen(function* () {
16131
+ const make$110 = Effect.gen(function* () {
16043
16132
  const sql = yield* SqlClient.SqlClient;
16044
16133
  const createPairingLinkRow = SqlSchema.void({
16045
16134
  Request: CreateAuthPairingLinkInput,
@@ -16174,7 +16263,7 @@ const make$109 = Effect.gen(function* () {
16174
16263
  getByCredential
16175
16264
  };
16176
16265
  });
16177
- const layer$112 = Layer.effect(AuthPairingLinkRepository, make$109);
16266
+ const layer$113 = Layer.effect(AuthPairingLinkRepository, make$110);
16178
16267
  //#endregion
16179
16268
  //#region src/auth/PairingGrantStore.ts
16180
16269
  var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
@@ -16269,7 +16358,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
16269
16358
  const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
16270
16359
  const PAIRING_TOKEN_LENGTH = 12;
16271
16360
  const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
16272
- const make$108 = Effect.gen(function* () {
16361
+ const make$109 = Effect.gen(function* () {
16273
16362
  const crypto = yield* Crypto.Crypto;
16274
16363
  const config = yield* ServerConfig$1;
16275
16364
  const pairingLinks = yield* AuthPairingLinkRepository;
@@ -16467,7 +16556,7 @@ const make$108 = Effect.gen(function* () {
16467
16556
  consume
16468
16557
  });
16469
16558
  });
16470
- const layer$111 = Layer.effect(PairingGrantStore, make$108).pipe(Layer.provideMerge(layer$112));
16559
+ const layer$112 = Layer.effect(PairingGrantStore, make$109).pipe(Layer.provideMerge(layer$113));
16471
16560
  //#endregion
16472
16561
  //#region src/persistence/DatabaseSnapshot.ts
16473
16562
  /**
@@ -16599,6 +16688,11 @@ const discardDatabaseSnapshot = Effect.fn("discardDatabaseSnapshot")(function* (
16599
16688
  yield* fs.remove(paths.stagingPath, { force: true });
16600
16689
  });
16601
16690
  //#endregion
16691
+ //#region src/persistence/Migrations/059_FusionReviewExperiment.ts
16692
+ var _059_FusionReviewExperiment_default = Effect.gen(function* () {
16693
+ yield* (yield* SqlClient.SqlClient)`ALTER TABLE thread_pairs ADD COLUMN review_experiment_json TEXT`;
16694
+ });
16695
+ //#endregion
16602
16696
  //#region src/persistence/Migrations/001_OrchestrationEvents.ts
16603
16697
  var _001_OrchestrationEvents_default = Effect.gen(function* () {
16604
16698
  const sql = yield* SqlClient.SqlClient;
@@ -18770,6 +18864,11 @@ const migrationEntries = [
18770
18864
  58,
18771
18865
  "HubActivityOutbound",
18772
18866
  _058_HubActivityOutbound_default
18867
+ ],
18868
+ [
18869
+ 59,
18870
+ "FusionReviewExperiment",
18871
+ _059_FusionReviewExperiment_default
18773
18872
  ]
18774
18873
  ];
18775
18874
  const makeMigrationLoader = (throughId) => Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
@@ -19798,7 +19897,7 @@ function parseBearerToken(request) {
19798
19897
  const token = header.slice(7).trim();
19799
19898
  return token.length > 0 ? token : null;
19800
19899
  }
19801
- const make$107 = Effect.gen(function* () {
19900
+ const make$108 = Effect.gen(function* () {
19802
19901
  const policy = yield* EnvironmentAuthPolicy;
19803
19902
  const bootstrapCredentials = yield* PairingGrantStore;
19804
19903
  const sessions = yield* SessionStore;
@@ -19993,9 +20092,9 @@ const make$107 = Effect.gen(function* () {
19993
20092
  issueStartupPairingUrl
19994
20093
  });
19995
20094
  });
19996
- const layer$110 = Layer.effect(EnvironmentAuth, make$107).pipe(Layer.provideMerge(layer$111), Layer.provideMerge(layer$113), Layer.provideMerge(layer$116));
19997
- const storageLayer = Layer.mergeAll(layer$114, layerConfig);
19998
- const runtimeLayer = layer$110.pipe(Layer.provideMerge(storageLayer));
20095
+ const layer$111 = Layer.effect(EnvironmentAuth, make$108).pipe(Layer.provideMerge(layer$112), Layer.provideMerge(layer$114), Layer.provideMerge(layer$117));
20096
+ const storageLayer = Layer.mergeAll(layer$115, layerConfig);
20097
+ const runtimeLayer = layer$111.pipe(Layer.provideMerge(storageLayer));
19999
20098
  //#endregion
20000
20099
  //#region ../../packages/shared/src/Struct.ts
20001
20100
  function deepMerge(current, patch) {
@@ -21843,7 +21942,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
21843
21942
  }
21844
21943
  return yield* Effect.gen(function* () {
21845
21944
  return yield* run(yield* EnvironmentAuth);
21846
- }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$117(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
21945
+ }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$118(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
21847
21946
  });
21848
21947
  const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
21849
21948
  const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
@@ -21989,7 +22088,7 @@ const DEFAULT_LIMITS = {
21989
22088
  windowMillis: FAILURE_WINDOW_MS$1,
21990
22089
  blockMillis: BLOCK_DURATION_MS
21991
22090
  };
21992
- const make$106 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
22091
+ const make$107 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
21993
22092
  const state = yield* Ref.make(initialThrottleState);
21994
22093
  return HubAuthThrottle.of({
21995
22094
  shouldRefuse: Effect.gen(function* () {
@@ -22003,7 +22102,7 @@ const make$106 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_L
22003
22102
  })
22004
22103
  });
22005
22104
  });
22006
- const layer$109 = Layer.effect(HubAuthThrottle, make$106());
22105
+ const layer$110 = Layer.effect(HubAuthThrottle, make$107());
22007
22106
  //#endregion
22008
22107
  //#region src/hub/HubAuth.ts
22009
22108
  /**
@@ -22771,7 +22870,7 @@ const TaskRepositoryLive = Layer.effect(TaskRepository, makeTaskRepository);
22771
22870
  //#region src/hub/HubClientRevocations.ts
22772
22871
  var HubClientRevocations = class extends Context.Service()("@p4code/cli/hub/HubClientRevocations") {};
22773
22872
  /** Coalesced invalidations carry no identity or credential; consumers recheck durable state. */
22774
- const layer$108 = Layer.effect(HubClientRevocations, Effect.gen(function* () {
22873
+ const layer$109 = Layer.effect(HubClientRevocations, Effect.gen(function* () {
22775
22874
  const changes = yield* PubSub.sliding(1);
22776
22875
  yield* Effect.addFinalizer(() => PubSub.shutdown(changes));
22777
22876
  return HubClientRevocations.of({
@@ -23009,7 +23108,7 @@ const hubClientPreflight = Effect.gen(function* () {
23009
23108
  }
23010
23109
  });
23011
23110
  });
23012
- const layer$107 = Layer.mergeAll(Layer.empty, ...Object.keys(CLIENT_ROUTES).map((path) => HttpRouter.add("OPTIONS", path, hubClientPreflight)));
23111
+ const layer$108 = Layer.mergeAll(Layer.empty, ...Object.keys(CLIENT_ROUTES).map((path) => HttpRouter.add("OPTIONS", path, hubClientPreflight)));
23013
23112
  const HUB_TUNNEL_MAX_FRAME_PAYLOAD_BYTES = 64 * 1024;
23014
23113
  const HUB_TUNNEL_MAX_MESSAGE_BYTES = 128 * 1024 * 1024;
23015
23114
  const HUB_TUNNEL_FRAME_MAGIC = 1345606705;
@@ -23296,7 +23395,7 @@ var HubTunnelConnectorBudget = class {
23296
23395
  const HUB_TUNNEL_RETIRE_TIMEOUT_MS = 1e4;
23297
23396
  var HubTunnelUnavailable = class extends Data.TaggedError("HubTunnelUnavailable") {};
23298
23397
  var HubTunnelRegistry = class extends Context.Service()("@p4code/cli/hub/HubTunnelRegistry") {};
23299
- const layer$106 = Layer.effect(HubTunnelRegistry, Effect.gen(function* () {
23398
+ const layer$107 = Layer.effect(HubTunnelRegistry, Effect.gen(function* () {
23300
23399
  const config = yield* HubConfig;
23301
23400
  const lock = yield* Semaphore.make(1);
23302
23401
  const entries = /* @__PURE__ */ new Map();
@@ -23549,7 +23648,7 @@ const makeHubClientSocketTickets = Effect.gen(function* () {
23549
23648
  };
23550
23649
  });
23551
23650
  var HubClientSocketTickets = class extends Context.Service()("@p4code/cli/hub/HubClientSocketTickets") {};
23552
- const layer$105 = Layer.effect(HubClientSocketTickets, makeHubClientSocketTickets);
23651
+ const layer$106 = Layer.effect(HubClientSocketTickets, makeHubClientSocketTickets);
23553
23652
  //#endregion
23554
23653
  //#region src/hub/HubClientLease.ts
23555
23654
  /** Other Hub processes sharing the database are observed within this interval. */
@@ -23705,7 +23804,7 @@ const socketTicket = handle$1(Effect.gen(function* () {
23705
23804
  }
23706
23805
  }));
23707
23806
  }), "client-route");
23708
- const layer$104 = Layer.mergeAll(layer$107, HttpRouter.add("POST", "/v1/clients/me/socket-ticket", withHubClientCors(socketTicket)), HttpRouter.add("POST", "/v1/client-enrollments", issue$1), HttpRouter.add("POST", "/v1/client-enrollments/exchange", withHubClientCors(exchange$1)), HttpRouter.add("GET", "/v1/clients/me", withHubClientCors(identity$2)), HttpRouter.add("GET", "/v1/clients/me/events", withHubClientCors(events)), HttpRouter.add("DELETE", "/v1/clients/me", withHubClientCors(signOut)), HttpRouter.add("POST", "/v1/clients/revoke", revoke));
23807
+ const layer$105 = Layer.mergeAll(layer$108, HttpRouter.add("POST", "/v1/clients/me/socket-ticket", withHubClientCors(socketTicket)), HttpRouter.add("POST", "/v1/client-enrollments", issue$1), HttpRouter.add("POST", "/v1/client-enrollments/exchange", withHubClientCors(exchange$1)), HttpRouter.add("GET", "/v1/clients/me", withHubClientCors(identity$2)), HttpRouter.add("GET", "/v1/clients/me/events", withHubClientCors(events)), HttpRouter.add("DELETE", "/v1/clients/me", withHubClientCors(signOut)), HttpRouter.add("POST", "/v1/clients/revoke", revoke));
23709
23808
  //#endregion
23710
23809
  //#region src/hub/HubPushTokenProtection.ts
23711
23810
  const KEY_NAME = "hub-push-token-encryption-v1";
@@ -23985,12 +24084,12 @@ const persistHubAgentActivity = Effect.fn("HubNotificationActivity.persist")(fun
23985
24084
  var HubSecretStoreUnavailable = class extends Data.TaggedError("HubSecretStoreUnavailable") {};
23986
24085
  const HUB_SECRETS_SUFFIX = ".secrets";
23987
24086
  /** Dedicated sibling directory per Hub database; no environment configuration is consulted. */
23988
- const make$105 = Effect.gen(function* () {
24087
+ const make$106 = Effect.gen(function* () {
23989
24088
  const config = yield* HubConfig;
23990
24089
  if (!(yield* Path$1.Path).isAbsolute(config.dbPath)) return yield* Effect.fail(new HubSecretStoreUnavailable({ message: "Hub secret storage requires an absolute database path." }));
23991
24090
  return yield* makeForDirectory(`${config.dbPath}${HUB_SECRETS_SUFFIX}`);
23992
24091
  });
23993
- const layer$103 = Layer.effect(ServerSecretStore, make$105);
24092
+ const layer$104 = Layer.effect(ServerSecretStore, make$106);
23994
24093
  //#endregion
23995
24094
  //#region src/hub/HubNotificationRoutes.ts
23996
24095
  const MAX_NOTIFICATION_BODY_BYTES = 16 * 1024;
@@ -24034,7 +24133,7 @@ const makeRoutes = Effect.gen(function* () {
24034
24133
  }))));
24035
24134
  });
24036
24135
  /** Launch-only composition: isolated Hub HTTP tests need no filesystem credential store. */
24037
- const layer$102 = Layer.unwrap(makeRoutes).pipe(Layer.provide(layer$103));
24136
+ const layer$103 = Layer.unwrap(makeRoutes).pipe(Layer.provide(layer$104));
24038
24137
  //#endregion
24039
24138
  //#region src/hub/HubApnsCredentials.ts
24040
24139
  const HUB_APNS_CREDENTIAL_SECRET = "hub-apns-credentials-v1";
@@ -24605,7 +24704,7 @@ const MAX_APNS_REQUEST_BYTES = 4096;
24605
24704
  const CLEANUP_TIMEOUT_MS$1 = 1e3;
24606
24705
  const MAX_REQUEST_BYTES$2 = 8 * 1024;
24607
24706
  const MAX_RESPONSE_BYTES$8 = 16 * 1024;
24608
- const REQUEST_TIMEOUT_MS$6 = 8e3;
24707
+ const REQUEST_TIMEOUT_MS$7 = 8e3;
24609
24708
  const MAX_AUTH_CHARS = 8192;
24610
24709
  const unavailable$6 = () => ({
24611
24710
  outcome: "retry",
@@ -24650,7 +24749,7 @@ const sendHubApns = Effect.fn("HubPushTransport.apns")(function* (input) {
24650
24749
  finish(unavailable$6());
24651
24750
  return;
24652
24751
  }
24653
- timer = setTimeout(() => finish(unavailable$6()), REQUEST_TIMEOUT_MS$6);
24752
+ timer = setTimeout(() => finish(unavailable$6()), REQUEST_TIMEOUT_MS$7);
24654
24753
  session = NodeHttp2.connect(input.sandbox ? "https://api.sandbox.push.apple.com" : "https://api.push.apple.com", { maxHeaderListPairs: 32 });
24655
24754
  session.on("error", () => finish(unavailable$6()));
24656
24755
  session.on("close", () => finish(unavailable$6()));
@@ -24705,7 +24804,7 @@ const sendHubFcm = Effect.fn("HubPushTransport.fcm")(function* (input, fetcher =
24705
24804
  const abort = () => owner.abort();
24706
24805
  signal.addEventListener("abort", abort, { once: true });
24707
24806
  if (signal.aborted) owner.abort();
24708
- const timeout = setTimeout(abort, REQUEST_TIMEOUT_MS$6);
24807
+ const timeout = setTimeout(abort, REQUEST_TIMEOUT_MS$7);
24709
24808
  let reader;
24710
24809
  try {
24711
24810
  const response = await fetcher(`https://fcm.googleapis.com/v1/projects/${input.projectId}/messages:send`, {
@@ -24746,7 +24845,7 @@ const sendHubFcm = Effect.fn("HubPushTransport.fcm")(function* (input, fetcher =
24746
24845
  }
24747
24846
  },
24748
24847
  catch: () => unavailable$6()
24749
- }).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$6), Effect.catch(() => Effect.succeed(unavailable$6())));
24848
+ }).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$7), Effect.catch(() => Effect.succeed(unavailable$6())));
24750
24849
  });
24751
24850
  //#endregion
24752
24851
  //#region src/hub/HubNotificationRuntime.ts
@@ -24777,10 +24876,10 @@ const makeHubNotificationProvider = Effect.fn("HubNotificationRuntime.provider")
24777
24876
  });
24778
24877
  });
24779
24878
  const runHubNotificationSchedule = (batch) => batch.pipe(Effect.asVoid, Effect.catch(() => Effect.logWarning("Hub notification delivery unavailable.")), Effect.repeat(Schedule.spaced(Duration.millis(HUB_NOTIFICATION_INTERVAL_MS))));
24780
- const layer$101 = Layer.effectDiscard(Effect.gen(function* () {
24879
+ const layer$102 = Layer.effectDiscard(Effect.gen(function* () {
24781
24880
  const batch = yield* makeHubNotificationDelivery(yield* makeHubNotificationProvider());
24782
24881
  yield* runHubNotificationSchedule(batch()).pipe(Effect.forkScoped);
24783
- })).pipe(Layer.provide(layer$103));
24882
+ })).pipe(Layer.provide(layer$104));
24784
24883
  const HUB_RETIREMENT_TIMEOUT_MS = 1e4;
24785
24884
  const HUB_RETIREMENT_CLAIM_MS = 3e4;
24786
24885
  const HUB_RETIREMENT_TTL_MS = 1440 * 60 * 1e3;
@@ -24947,10 +25046,10 @@ const HUB_RETIREMENT_INTERVAL_MS = 5e3;
24947
25046
  /** One bounded batch per tick; persisted due times control retries, never an in-memory retry loop. */
24948
25047
  const runHubRetirementSchedule = (batch) => batch.pipe(Effect.asVoid, Effect.catch(() => Effect.logWarning("Hub retirement delivery unavailable.")), Effect.repeat(Schedule.spaced(Duration.millis(HUB_RETIREMENT_INTERVAL_MS))));
24949
25048
  /** Secret store remains private to notification runtime rather than becoming a general Hub dependency. */
24950
- const layer$100 = Layer.effectDiscard(Effect.gen(function* () {
25049
+ const layer$101 = Layer.effectDiscard(Effect.gen(function* () {
24951
25050
  const batch = yield* makeHubPushRetirementDelivery(yield* makeHubRetirementProvider());
24952
25051
  yield* runHubRetirementSchedule(batch()).pipe(Effect.forkScoped);
24953
- })).pipe(Layer.provide(layer$103));
25052
+ })).pipe(Layer.provide(layer$104));
24954
25053
  /** One bounded keyset page; callers resume the receipt and restart from null after a full sweep. */
24955
25054
  const reconcileHubPushRegistrations = Effect.fn("HubPushReconciliation.page")(function* (afterClientId = null) {
24956
25055
  const sql = yield* SqlClient.SqlClient;
@@ -24993,7 +25092,7 @@ const sweepHubPushRegistrations = Effect.fn("HubPushReconciliation.sweep")(funct
24993
25092
  };
24994
25093
  });
24995
25094
  /** Scoped schedule prevents overlapping sweeps and cancels both page work and waiting on shutdown. */
24996
- const layer$99 = Layer.effectDiscard(Effect.gen(function* () {
25095
+ const layer$100 = Layer.effectDiscard(Effect.gen(function* () {
24997
25096
  yield* sweepHubPushRegistrations(reconcileHubPushRegistrations).pipe(Effect.catch(() => Effect.logWarning("Hub notification reconciliation unavailable.")), Effect.repeat(Schedule.spaced(Duration.millis(HUB_PUSH_RECONCILIATION_INTERVAL_MS))), Effect.forkScoped);
24998
25097
  }));
24999
25098
  //#endregion
@@ -25024,7 +25123,7 @@ const hubMdxHost = Effect.gen(function* () {
25024
25123
  }
25025
25124
  });
25026
25125
  }).pipe(Effect.catch(() => Effect.succeed(HttpServerResponse.empty({ status: 503 }))));
25027
- const layer$98 = Layer.mergeAll(HttpRouter.add("GET", "/mdx-host.html", hubMdxHost), HttpRouter.add("GET", "/mdx-jsx-runtime.js", hubMdxHost), HttpRouter.add("GET", "/assets/:file", hubMdxHost));
25126
+ const layer$99 = Layer.mergeAll(HttpRouter.add("GET", "/mdx-host.html", hubMdxHost), HttpRouter.add("GET", "/mdx-jsx-runtime.js", hubMdxHost), HttpRouter.add("GET", "/assets/:file", hubMdxHost));
25028
25127
  //#endregion
25029
25128
  //#region ../../packages/shared/src/hubAssetGrant.ts
25030
25129
  /** Managed assets retain the environment's signed grant root for relative preview resources. */
@@ -25109,7 +25208,7 @@ const makeHubClientAssetGrants = Effect.gen(function* () {
25109
25208
  };
25110
25209
  });
25111
25210
  var HubClientAssetGrants = class extends Context.Service()("@p4code/cli/hub/HubClientAssetGrants") {};
25112
- const layer$97 = Layer.effect(HubClientAssetGrants, makeHubClientAssetGrants);
25211
+ const layer$98 = Layer.effect(HubClientAssetGrants, makeHubClientAssetGrants);
25113
25212
  //#endregion
25114
25213
  //#region src/hub/HubTunnelHttpRequestProducer.ts
25115
25214
  /** Consumes the ingress request lazily under its stream producer; transport admission owns frame copies. */
@@ -25411,7 +25510,7 @@ const writeHubTunnelNativeResponse = Effect.fn("HubTunnelNativeResponse.write")(
25411
25510
  //#region src/hub/HubTunnelPublished.ts
25412
25511
  const MAX_STREAM_ID$1 = 4294967295;
25413
25512
  var HubTunnelPublished = class extends Context.Service()("@p4code/cli/hub/HubTunnelPublished") {};
25414
- const layer$96 = Layer.effect(HubTunnelPublished, Effect.gen(function* () {
25513
+ const layer$97 = Layer.effect(HubTunnelPublished, Effect.gen(function* () {
25415
25514
  const registry = yield* HubTunnelRegistry;
25416
25515
  const entries = /* @__PURE__ */ new Map();
25417
25516
  const current = (lease) => Effect.gen(function* () {
@@ -25597,7 +25696,7 @@ const hubTunnelHttpRoute = Effect.gen(function* () {
25597
25696
  const response = yield* authenticatedRoute;
25598
25697
  return HttpServerResponse.setHeaders(response, cors);
25599
25698
  });
25600
- const layer$95 = HttpRouter.add("*", `${HUB_TUNNEL_HTTP_PATH}/*`, hubTunnelHttpRoute);
25699
+ const layer$96 = HttpRouter.add("*", `${HUB_TUNNEL_HTTP_PATH}/*`, hubTunnelHttpRoute);
25601
25700
  //#endregion
25602
25701
  //#region src/hub/HubAssetRoutes.ts
25603
25702
  const MAX_GRANT_BODY_BYTES = 8192;
@@ -25657,7 +25756,7 @@ const hubAssetRoute = Effect.gen(function* () {
25657
25756
  }
25658
25757
  }))));
25659
25758
  });
25660
- const layer$94 = Layer.mergeAll(HttpRouter.add("POST", "/v1/clients/me/asset-grant", issue), HttpRouter.add("*", `${HUB_ASSET_PATH}/*`, hubAssetRoute));
25759
+ const layer$95 = Layer.mergeAll(HttpRouter.add("POST", "/v1/clients/me/asset-grant", issue), HttpRouter.add("*", `${HUB_ASSET_PATH}/*`, hubAssetRoute));
25661
25760
  //#endregion
25662
25761
  //#region src/hub/HubComputerUseIdentity.ts
25663
25762
  /** A server identity is not a helper identity or a client pairing credential. */
@@ -25745,7 +25844,7 @@ const readBody = Effect.fn("hub.computerUse.readBody")(function* (request) {
25745
25844
  return (yield* request.stream.pipe(Stream.runFoldEffect(() => Buffer.alloc(0), (body, chunk) => body.byteLength + chunk.byteLength > MAX_REQUEST_BYTES$1 ? Effect.fail(new HubComputerUseRoutesFailure({ message: "Computer Use enrollment body exceeds limit" })) : Effect.succeed(Buffer.concat([body, chunk]))))).toString("utf8");
25746
25845
  });
25747
25846
  /** Server-to-server delegation only. A Hub identity does not authenticate an ordinary environment client. */
25748
- const layer$93 = Layer.unwrap(Effect.gen(function* () {
25847
+ const layer$94 = Layer.unwrap(Effect.gen(function* () {
25749
25848
  const enrollments = makeHubComputerUseEnrollments(yield* HubConfig);
25750
25849
  const handle = Effect.fn("hub.computerUse.handle")(function* (operation) {
25751
25850
  const environmentId = yield* authenticateHubComputerUseServer();
@@ -26554,7 +26653,7 @@ const hubTunnelConnectorRoute = Effect.gen(function* () {
26554
26653
  status: 503,
26555
26654
  headers: { "cache-control": "no-store" }
26556
26655
  }))));
26557
- const layer$92 = HttpRouter.add("GET", HUB_TUNNEL_CONNECTOR_PATH, hubTunnelConnectorRoute);
26656
+ const layer$93 = HttpRouter.add("GET", HUB_TUNNEL_CONNECTOR_PATH, hubTunnelConnectorRoute);
26558
26657
  const TICKET_PATTERN = /^[a-f0-9]{64}$/;
26559
26658
  const MAX_TICKETED_URL_CHARS = 8512;
26560
26659
  /** Remove only Hub transport authentication; retain environment query bytes and order exactly. */
@@ -26745,7 +26844,7 @@ const hubTunnelClientSocketRoute = Effect.gen(function* () {
26745
26844
  }
26746
26845
  return unavailable$5();
26747
26846
  })));
26748
- const layer$91 = HttpRouter.add("GET", `${HUB_TUNNEL_CLIENT_SOCKET_PATH}/*`, hubTunnelClientSocketRoute);
26847
+ const layer$92 = HttpRouter.add("GET", `${HUB_TUNNEL_CLIENT_SOCKET_PATH}/*`, hubTunnelClientSocketRoute);
26749
26848
  //#endregion
26750
26849
  //#region src/hub/HubHttpServer.ts
26751
26850
  /**
@@ -27242,8 +27341,8 @@ const releaseFeedLeaseRoute = HttpRouter.add("DELETE", "/feed/lease", respondToH
27242
27341
  return HttpServerResponse.empty({ status: 204 });
27243
27342
  })));
27244
27343
  /** Provide around HttpRouter.serve so request fibers share these process-owned services. */
27245
- const routingServices = Layer.mergeAll(layer$108, layer$105, layer$97, layer$96).pipe(Layer.provideMerge(layer$106));
27246
- const layer$90 = Layer.mergeAll(layer$95, layer$94, layer$98, layer$92, layer$91, layer$104, layer$93, healthRoute, identityRoute, listTasksRoute, getTaskRoute, createTaskRoute, putTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute, listFeedSourcesRoute, upsertFeedSourceRoute, deleteFeedSourceRoute, listFeedRoute, putFeedArticleRoute, putFeedEngagementRoute, markFeedReadRoute, cleanupFeedRoute, setFeedRefreshedAtRoute, acquireFeedLeaseRoute, renewFeedLeaseRoute, releaseFeedLeaseRoute).pipe(Layer.provideMerge(layer$108), Layer.provideMerge(layer$105), Layer.provideMerge(layer$97));
27344
+ const routingServices = Layer.mergeAll(layer$109, layer$106, layer$98, layer$97).pipe(Layer.provideMerge(layer$107));
27345
+ const layer$91 = Layer.mergeAll(layer$96, layer$95, layer$99, layer$93, layer$92, layer$105, layer$94, healthRoute, identityRoute, listTasksRoute, getTaskRoute, createTaskRoute, putTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute, listFeedSourcesRoute, upsertFeedSourceRoute, deleteFeedSourceRoute, listFeedRoute, putFeedArticleRoute, putFeedEngagementRoute, markFeedReadRoute, cleanupFeedRoute, setFeedRefreshedAtRoute, acquireFeedLeaseRoute, renewFeedLeaseRoute, releaseFeedLeaseRoute).pipe(Layer.provideMerge(layer$109), Layer.provideMerge(layer$106), Layer.provideMerge(layer$98));
27247
27346
  //#endregion
27248
27347
  //#region src/hub/Migrations/001_Tasks.ts
27249
27348
  /**
@@ -27931,7 +28030,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
27931
28030
  });
27932
28031
  if (isRemoteReachableHost(config.host)) yield* Effect.logWarning("Hub is reachable beyond loopback. The bearer token is the only control — repeated auth failures are not rate limited.", { host: config.host });
27933
28032
  }));
27934
- const makeHubLayer = HttpRouter.serve(Layer.mergeAll(layer$90, layer$102)).pipe(Layer.provideMerge(routingServices), Layer.provideMerge(layer$100), Layer.provideMerge(layer$101), Layer.provideMerge(layer$99), Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$109), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
28033
+ const makeHubLayer = HttpRouter.serve(Layer.mergeAll(layer$91, layer$103)).pipe(Layer.provideMerge(routingServices), Layer.provideMerge(layer$101), Layer.provideMerge(layer$102), Layer.provideMerge(layer$100), Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$110), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
27935
28034
  const runHub = Layer.launch(makeHubLayer);
27936
28035
  //#endregion
27937
28036
  //#region src/atomicWrite.ts
@@ -28062,7 +28161,7 @@ function stripDefaultServerSettings(current, defaults) {
28062
28161
  }
28063
28162
  return Object.is(current, defaults) ? void 0 : current;
28064
28163
  }
28065
- const make$104 = Effect.gen(function* () {
28164
+ const make$105 = Effect.gen(function* () {
28066
28165
  const { settingsPath } = yield* ServerConfig$1;
28067
28166
  const fs = yield* FileSystem.FileSystem;
28068
28167
  const pathService = yield* Path$1.Path;
@@ -28284,7 +28383,7 @@ const make$104 = Effect.gen(function* () {
28284
28383
  }
28285
28384
  };
28286
28385
  });
28287
- const layer$89 = Layer.effect(ServerSettingsService, make$104);
28386
+ const layer$90 = Layer.effect(ServerSettingsService, make$105);
28288
28387
  //#endregion
28289
28388
  //#region src/pathExpansion.ts
28290
28389
  /**
@@ -28946,7 +29045,7 @@ function claudeEntryFromRegistration(registration) {
28946
29045
  };
28947
29046
  }
28948
29047
  var ClaudeMcpFiles = class extends Context.Service()("@p4code/cli/mcp/ClaudeMcpFiles") {};
28949
- const make$103 = Effect.gen(function* () {
29048
+ const make$104 = Effect.gen(function* () {
28950
29049
  const fileSystem = yield* FileSystem.FileSystem;
28951
29050
  const path = yield* Path$1.Path;
28952
29051
  const services = yield* Effect.context();
@@ -29013,7 +29112,7 @@ const make$103 = Effect.gen(function* () {
29013
29112
  removeProject: (projectDir, name) => removeAt(Effect.succeed(projectFile(projectDir)))(name)
29014
29113
  };
29015
29114
  });
29016
- const layer$88 = Layer.effect(ClaudeMcpFiles, make$103);
29115
+ const layer$89 = Layer.effect(ClaudeMcpFiles, make$104);
29017
29116
  Layer.succeed(ClaudeMcpFiles, {
29018
29117
  readUser: Effect.succeed([]),
29019
29118
  readUserAt: () => Effect.succeed([]),
@@ -29151,7 +29250,7 @@ const decodeClientRegistration = Schema$1.decodeUnknownExit(ClientRegistrationRe
29151
29250
  const decodeTokenResponse = Schema$1.decodeUnknownExit(TokenResponse);
29152
29251
  var McpOAuth = class extends Context.Service()("@p4code/cli/mcp/McpOAuth") {};
29153
29252
  const registryError = (detail) => new McpRegistryError({ detail });
29154
- const make$102 = Effect.gen(function* () {
29253
+ const make$103 = Effect.gen(function* () {
29155
29254
  const config = yield* ServerConfig$1;
29156
29255
  const secrets = yield* ServerSecretStore;
29157
29256
  const http = yield* HttpClient.HttpClient;
@@ -29471,7 +29570,7 @@ const make$102 = Effect.gen(function* () {
29471
29570
  accessTokenFor
29472
29571
  };
29473
29572
  });
29474
- const layer$87 = Layer.effect(McpOAuth, make$102);
29573
+ const layer$88 = Layer.effect(McpOAuth, make$103);
29475
29574
  Layer.succeed(McpOAuth, {
29476
29575
  statusFor: () => Effect.succeed(Option.none()),
29477
29576
  begin: () => Effect.fail(new McpRegistryError({ detail: "OAuth sign-in is not available." })),
@@ -29489,7 +29588,7 @@ const decodeRegistration$1 = Schema$1.decodeUnknownExit(RegistrationFromJson$1);
29489
29588
  const encodeRegistration = Schema$1.encodeSync(RegistrationFromJson$1);
29490
29589
  var McpRegistry = class extends Context.Service()("@p4code/cli/mcp/McpRegistry") {};
29491
29590
  const slotsOf = (registration) => registration.secrets ?? [];
29492
- const make$101 = Effect.gen(function* () {
29591
+ const make$102 = Effect.gen(function* () {
29493
29592
  const config = yield* ServerConfig$1;
29494
29593
  const secrets = yield* ServerSecretStore;
29495
29594
  const oauth = yield* McpOAuth;
@@ -29663,7 +29762,7 @@ const make$101 = Effect.gen(function* () {
29663
29762
  resolveForSessionAtClaudeUserConfigPath
29664
29763
  };
29665
29764
  });
29666
- const layer$86 = Layer.effect(McpRegistry, make$101);
29765
+ const layer$87 = Layer.effect(McpRegistry, make$102);
29667
29766
  //#endregion
29668
29767
  //#region src/sync/skillDirectory.ts
29669
29768
  /**
@@ -30042,7 +30141,7 @@ const formatHubLink = (input) => encodeStoredHubLink({
30042
30141
  shareMode: input.shareMode
30043
30142
  });
30044
30143
  const fromEnvironment = (environment) => validateHubLink(environment.P4CODE_HUB_URL ?? "", environment.P4CODE_HUB_TOKEN ?? "");
30045
- const make$100 = Effect.fn("HubLink.make")(function* (environment) {
30144
+ const make$101 = Effect.fn("HubLink.make")(function* (environment) {
30046
30145
  const secrets = yield* ServerSecretStore;
30047
30146
  const env = environment ?? process.env;
30048
30147
  const fromEnv = fromEnvironment(env);
@@ -30115,7 +30214,7 @@ const make$100 = Effect.fn("HubLink.make")(function* (environment) {
30115
30214
  })
30116
30215
  };
30117
30216
  });
30118
- const layer$85 = Layer.effect(HubLink, make$100());
30217
+ const layer$86 = Layer.effect(HubLink, make$101());
30119
30218
  //#endregion
30120
30219
  //#region src/sync/HubAssetClient.ts
30121
30220
  /**
@@ -30148,7 +30247,7 @@ const decodeAssetListPage = Schema$1.decodeUnknownEffect(AssetListPage);
30148
30247
  const decodeConflictBody$1 = Schema$1.decodeUnknownEffect(ConflictBody$1);
30149
30248
  const decodeAsset = Schema$1.decodeUnknownEffect(AgentAsset);
30150
30249
  var HubAssetClient = class extends Context.Service()("@p4code/cli/sync/HubAssetClient") {};
30151
- const make$99 = Effect.gen(function* () {
30250
+ const make$100 = Effect.gen(function* () {
30152
30251
  const http = yield* HttpClient.HttpClient;
30153
30252
  const link = yield* HubLink;
30154
30253
  const requireSettings = Effect.gen(function* () {
@@ -30230,7 +30329,7 @@ const make$99 = Effect.gen(function* () {
30230
30329
  remove
30231
30330
  };
30232
30331
  });
30233
- const layer$84 = Layer.effect(HubAssetClient, make$99);
30332
+ const layer$85 = Layer.effect(HubAssetClient, make$100);
30234
30333
  //#endregion
30235
30334
  //#region src/sync/mcpRegistrationFiles.ts
30236
30335
  /**
@@ -31146,7 +31245,7 @@ const EMPTY_REPORT = {
31146
31245
  unavailable: null
31147
31246
  };
31148
31247
  var AssetSync = class extends Context.Service()("@p4code/cli/sync/AssetSync") {};
31149
- const make$98 = Effect.gen(function* () {
31248
+ const make$99 = Effect.gen(function* () {
31150
31249
  const client = yield* HubAssetClient;
31151
31250
  const link = yield* HubLink;
31152
31251
  const settingsStore = yield* ServerSettingsService;
@@ -31992,7 +32091,7 @@ const make$98 = Effect.gen(function* () {
31992
32091
  removeLocal
31993
32092
  };
31994
32093
  });
31995
- const layer$83 = Layer.effect(AssetSync, make$98);
32094
+ const layer$84 = Layer.effect(AssetSync, make$99);
31996
32095
  //#endregion
31997
32096
  //#region src/provider/CompressPrompts.ts
31998
32097
  /**
@@ -32345,7 +32444,7 @@ const findAgentPreset = (requested) => {
32345
32444
  };
32346
32445
  //#endregion
32347
32446
  //#region src/cli/assets.ts
32348
- const SyncServicesLive = layer$83.pipe(Layer.provideMerge(layer$84), Layer.provideMerge(layer$85), Layer.provideMerge(layer$89.pipe(Layer.provide(layer$114))), Layer.provideMerge(layer$114), Layer.provideMerge(FetchHttpClient.layer));
32447
+ const SyncServicesLive = layer$84.pipe(Layer.provideMerge(layer$85), Layer.provideMerge(layer$86), Layer.provideMerge(layer$90.pipe(Layer.provide(layer$115))), Layer.provideMerge(layer$115), Layer.provideMerge(FetchHttpClient.layer));
32349
32448
  const runWithSync = (flags, run) => Effect.gen(function* () {
32350
32449
  const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
32351
32450
  return yield* Effect.gen(function* () {
@@ -32353,7 +32452,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
32353
32452
  sync: yield* AssetSync,
32354
32453
  link: yield* HubLink
32355
32454
  });
32356
- }).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$117(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
32455
+ }).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$118(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
32357
32456
  });
32358
32457
  const STATE_LABEL = {
32359
32458
  synced: "synced",
@@ -33381,7 +33480,7 @@ const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
33381
33480
  "interrupted"
33382
33481
  ]);
33383
33482
  var ThreadBackgroundLivenessService = class extends Context.Service()("@p4code/cli/orchestration/ThreadBackgroundLiveness/ThreadBackgroundLivenessService") {};
33384
- function make$97() {
33483
+ function make$98() {
33385
33484
  const stateByThreadId = /* @__PURE__ */ new Map();
33386
33485
  const stateFor = (threadId) => {
33387
33486
  const existing = stateByThreadId.get(threadId);
@@ -33437,7 +33536,7 @@ function make$97() {
33437
33536
  }
33438
33537
  };
33439
33538
  }
33440
- const layer$82 = Layer.effect(ThreadBackgroundLivenessService, Effect.sync(make$97));
33539
+ const layer$83 = Layer.effect(ThreadBackgroundLivenessService, Effect.sync(make$98));
33441
33540
  //#endregion
33442
33541
  //#region ../../packages/shared/src/sourceControl.ts
33443
33542
  const GITHUB_CHANGE_REQUEST_PRESENTATION = {
@@ -33811,6 +33910,55 @@ function completionAfterEvent(thread, event) {
33811
33910
  }
33812
33911
  }
33813
33912
  //#endregion
33913
+ //#region src/orchestration/fusionReviewPolicy.ts
33914
+ function fusionBudgetAction(pair, threadId, text) {
33915
+ if (threadId === pair.watcherThreadId && (text.startsWith("[fusion-review]") || text.startsWith("[fusion-gate]"))) return "review";
33916
+ if (threadId === pair.implementerThreadId && text.startsWith("[fusion-advice]")) return "correction";
33917
+ if ([
33918
+ "[fusion-activation]",
33919
+ "[fusion-advice]",
33920
+ "[fusion-gate]",
33921
+ "[fusion-review]"
33922
+ ].some((prefix) => text.startsWith(prefix))) return "protocol";
33923
+ return "human";
33924
+ }
33925
+ function advanceFusionBudget(pair, threadId, text, sequence, commandId = "client") {
33926
+ if (!pair.reviewExperiment || pair.detachedAt !== null || sequence <= (pair.reviewExperiment.lastMessageSequence ?? 0)) return pair.reviewExperiment;
33927
+ const experiment = {
33928
+ ...pair.reviewExperiment,
33929
+ lastMessageSequence: sequence
33930
+ };
33931
+ switch (fusionBudgetAction(pair, threadId, text)) {
33932
+ case "review": return {
33933
+ ...experiment,
33934
+ automaticReviews: experiment.automaticReviews + 1
33935
+ };
33936
+ case "correction": return experiment.automaticReviews === 0 ? experiment : {
33937
+ ...experiment,
33938
+ correctionRounds: experiment.correctionRounds + 1
33939
+ };
33940
+ case "human":
33941
+ if (commandId === null || [
33942
+ "server:",
33943
+ "provider:",
33944
+ "scheduled-task:"
33945
+ ].some((prefix) => commandId.startsWith(prefix))) return experiment;
33946
+ return {
33947
+ ...experiment,
33948
+ automaticReviews: 0,
33949
+ correctionRounds: 0
33950
+ };
33951
+ case "protocol": return experiment;
33952
+ }
33953
+ }
33954
+ function fusionBudgetBlock(pair, threadId, text) {
33955
+ const experiment = pair.reviewExperiment;
33956
+ if (!experiment || pair.detachedAt !== null) return void 0;
33957
+ const action = fusionBudgetAction(pair, threadId, text);
33958
+ if (action === "review" && experiment.automaticReviews >= experiment.policy.maxAutomaticReviews) return `Automatic review limit (${experiment.policy.maxAutomaticReviews}) reached. Work remains unapproved; send a new instruction to either thread to resume.`;
33959
+ if (action === "correction" && experiment.automaticReviews > 0 && (experiment.correctionRounds >= experiment.policy.maxCorrectionRounds || experiment.automaticReviews >= experiment.policy.maxAutomaticReviews)) return "Correction budget exhausted or no review remains for another correction. Work remains unapproved; send a new instruction to either thread to resume.";
33960
+ }
33961
+ //#endregion
33814
33962
  //#region src/attachmentPaths.ts
33815
33963
  function normalizeAttachmentRelativePath(rawRelativePath) {
33816
33964
  const normalized = NodePath.normalize(rawRelativePath).replace(/^[/\\]+/, "");
@@ -34243,6 +34391,11 @@ function projectEvent(model, event) {
34243
34391
  watcherThreadId: payload.watcherThreadId,
34244
34392
  lastReviewedImplementerSequence: payload.lastReviewedImplementerSequence,
34245
34393
  activationTurnId: payload.activationTurnId ?? null,
34394
+ ...payload.reviewPolicy ? { reviewExperiment: {
34395
+ policy: payload.reviewPolicy,
34396
+ automaticReviews: 0,
34397
+ correctionRounds: 0
34398
+ } } : {},
34246
34399
  roundCap: payload.roundCap ?? 2,
34247
34400
  gateTimeoutMs: payload.gateTimeoutMs ?? 12e4,
34248
34401
  activeGate: null,
@@ -34514,6 +34667,10 @@ function projectEvent(model, event) {
34514
34667
  } : entry) : [...thread.messages, message]).slice(-2e3);
34515
34668
  return {
34516
34669
  ...nextBase,
34670
+ threadPairs: payload.role === "user" ? (nextBase.threadPairs ?? []).map((pair) => pair.reviewExperiment && pair.detachedAt === null && (pair.implementerThreadId === payload.threadId || pair.watcherThreadId === payload.threadId) ? {
34671
+ ...pair,
34672
+ reviewExperiment: advanceFusionBudget(pair, payload.threadId, payload.text, event.sequence, event.commandId) ?? pair.reviewExperiment
34673
+ } : pair) : nextBase.threadPairs ?? [],
34517
34674
  threads: updateThread(nextBase.threads, payload.threadId, {
34518
34675
  messages: cappedMessages,
34519
34676
  updatedAt: event.occurredAt
@@ -35110,6 +35267,7 @@ const decideCommand = Effect.fn("decideCommand")(function* ({ command, readModel
35110
35267
  lastReviewedImplementerSequence: readModel.snapshotSequence,
35111
35268
  activationTurnId: implementer.latestTurn?.state === "running" ? implementer.latestTurn.turnId : null,
35112
35269
  ...command.continuationMessageId !== void 0 ? { continuationMessageId: command.continuationMessageId } : {},
35270
+ ...command.reviewPolicy !== void 0 ? { reviewPolicy: command.reviewPolicy } : {},
35113
35271
  ...command.roundCap !== void 0 ? { roundCap: command.roundCap } : {},
35114
35272
  ...command.gateTimeoutMs !== void 0 ? { gateTimeoutMs: command.gateTimeoutMs } : {},
35115
35273
  createdAt: command.createdAt
@@ -35898,7 +36056,13 @@ const decideCommand = Effect.fn("decideCommand")(function* ({ command, readModel
35898
36056
  command,
35899
36057
  threadId: command.threadId
35900
36058
  });
35901
- if (findActiveThreadPair(readModel, command.threadId)?.watcherThreadId === command.threadId && targetThread.interactionMode !== "default") return yield* decideCommandSequence({
36059
+ const activePair = findActiveThreadPair(readModel, command.threadId);
36060
+ const budgetBlock = activePair && fusionBudgetBlock(activePair, command.threadId, command.message.text);
36061
+ if (budgetBlock) return yield* new OrchestrationCommandInvariantError({
36062
+ commandType: command.type,
36063
+ detail: budgetBlock
36064
+ });
36065
+ if (activePair?.watcherThreadId === command.threadId && targetThread.interactionMode !== "default") return yield* decideCommandSequence({
35902
36066
  readModel,
35903
36067
  commands: [{
35904
36068
  type: "thread.interaction-mode.set",
@@ -36497,6 +36661,7 @@ function commandToAggregateRef(command) {
36497
36661
  }
36498
36662
  const makeOrchestrationEngine = Effect.gen(function* () {
36499
36663
  const sql = yield* SqlClient.SqlClient;
36664
+ const settingsService = yield* Effect.serviceOption(ServerSettingsService);
36500
36665
  const eventStore = yield* OrchestrationEventStore;
36501
36666
  const commandReceiptRepository = yield* OrchestrationCommandReceiptRepository;
36502
36667
  const projectionPipeline = yield* OrchestrationProjectionPipeline;
@@ -36548,8 +36713,19 @@ const makeOrchestrationEngine = Effect.gen(function* () {
36548
36713
  detail: existingReceipt.value.error ?? "Previously rejected."
36549
36714
  });
36550
36715
  }
36716
+ let command = envelope.command;
36717
+ if (command.type === "thread-pair.create" && command.reviewPolicy === void 0 && Option.isSome(settingsService)) {
36718
+ const settings = yield* settingsService.value.getSettings.pipe(Effect.mapError(() => new OrchestrationCommandInvariantError({
36719
+ commandType: "thread-pair.create",
36720
+ detail: "Could not read the review policy settings."
36721
+ })));
36722
+ command = {
36723
+ ...command,
36724
+ reviewPolicy: settings.fusionReviewPolicy
36725
+ };
36726
+ }
36551
36727
  const eventBase = yield* decideOrchestrationCommand({
36552
- command: envelope.command,
36728
+ command,
36553
36729
  readModel: commandReadModel,
36554
36730
  hasLiveBackgroundWork: "threadId" in envelope.command && backgroundLiveness.getThreadBackgroundLiveness(envelope.command.threadId) !== null
36555
36731
  }).pipe(Effect.provideService(Crypto.Crypto, crypto), Effect.mapError((cause) => isOrchestrationCommandInvariantError(cause) ? cause : new OrchestrationCommandInvariantError({
@@ -36653,7 +36829,7 @@ const makeOrchestrationEngine = Effect.gen(function* () {
36653
36829
  latestSequence: Effect.sync(() => commandReadModel.snapshotSequence)
36654
36830
  };
36655
36831
  });
36656
- const OrchestrationEngineLive = Layer.effect(OrchestrationEngineService, makeOrchestrationEngine).pipe(Layer.provide(layer$82));
36832
+ const OrchestrationEngineLive = Layer.effect(OrchestrationEngineService, makeOrchestrationEngine).pipe(Layer.provide(layer$83));
36657
36833
  //#endregion
36658
36834
  //#region src/persistence/Services/ProjectionPendingApprovals.ts
36659
36835
  /**
@@ -38132,6 +38308,8 @@ const ProjectionThreadRepositoryLive = Layer.effect(ProjectionThreadRepository,
38132
38308
  * Counted rather than flagged so a lost completion can only strand one task,
38133
38309
  * and read as a boolean by the shell.
38134
38310
  */
38311
+ const decodeReviewExperiment = Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(FusionReviewExperiment));
38312
+ const encodeReviewExperiment = Schema$1.encodeEffect(Schema$1.fromJsonString(FusionReviewExperiment));
38135
38313
  const ORCHESTRATION_PROJECTOR_NAMES = {
38136
38314
  projects: "projection.projects",
38137
38315
  threads: "projection.threads",
@@ -38399,7 +38577,32 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
38399
38577
  });
38400
38578
  const applyThreadPairsProjection = Effect.fn("applyThreadPairsProjection")(function* (event, _attachmentSideEffects) {
38401
38579
  switch (event.type) {
38402
- case "thread-pair.created":
38580
+ case "thread.message-sent": {
38581
+ if (event.payload.role !== "user") return;
38582
+ const rows = yield* sql`
38583
+ SELECT pair_id AS id, implementer_thread_id AS "implementerThreadId", watcher_thread_id AS "watcherThreadId",
38584
+ review_experiment_json AS "experimentJson", created_at AS "createdAt"
38585
+ FROM thread_pairs WHERE detached_at IS NULL AND review_experiment_json IS NOT NULL
38586
+ AND (implementer_thread_id = ${event.payload.threadId} OR watcher_thread_id = ${event.payload.threadId})`.pipe(Effect.mapError(toPersistenceSqlError("ProjectionPipeline.fusionBudget:read")));
38587
+ for (const row of rows) {
38588
+ const experiment = yield* decodeReviewExperiment(row.experimentJson).pipe(Effect.orDie);
38589
+ const next = advanceFusionBudget({
38590
+ ...row,
38591
+ reviewExperiment: experiment,
38592
+ detachedAt: null
38593
+ }, event.payload.threadId, event.payload.text, event.sequence, event.commandId);
38594
+ if (!next) continue;
38595
+ const encoded = yield* encodeReviewExperiment(next).pipe(Effect.orDie);
38596
+ yield* sql`UPDATE thread_pairs SET review_experiment_json = ${encoded} WHERE pair_id = ${row.id}`.pipe(Effect.mapError(toPersistenceSqlError("ProjectionPipeline.fusionBudget:update")));
38597
+ }
38598
+ return;
38599
+ }
38600
+ case "thread-pair.created": {
38601
+ const experimentJson = event.payload.reviewPolicy ? yield* encodeReviewExperiment({
38602
+ policy: event.payload.reviewPolicy,
38603
+ automaticReviews: 0,
38604
+ correctionRounds: 0
38605
+ }).pipe(Effect.orDie) : null;
38403
38606
  yield* sql`
38404
38607
  INSERT INTO thread_pairs (
38405
38608
  pair_id,
@@ -38409,6 +38612,7 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
38409
38612
  activation_turn_id,
38410
38613
  round_cap,
38411
38614
  gate_timeout_ms,
38615
+ review_experiment_json,
38412
38616
  active_gate_json,
38413
38617
  created_at,
38414
38618
  detached_at
@@ -38420,6 +38624,7 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
38420
38624
  ${event.payload.activationTurnId ?? null},
38421
38625
  ${event.payload.roundCap ?? 2},
38422
38626
  ${event.payload.gateTimeoutMs ?? 12e4},
38627
+ ${experimentJson},
38423
38628
  NULL,
38424
38629
  ${event.payload.createdAt},
38425
38630
  NULL
@@ -38431,11 +38636,13 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
38431
38636
  activation_turn_id = excluded.activation_turn_id,
38432
38637
  round_cap = excluded.round_cap,
38433
38638
  gate_timeout_ms = excluded.gate_timeout_ms,
38639
+ review_experiment_json = excluded.review_experiment_json,
38434
38640
  active_gate_json = NULL,
38435
38641
  created_at = excluded.created_at,
38436
38642
  detached_at = NULL
38437
38643
  `.pipe(Effect.mapError(toPersistenceSqlError("ProjectionPipeline.threadPairs:create")));
38438
38644
  return;
38645
+ }
38439
38646
  case "thread-pair.gate-opened":
38440
38647
  case "thread-pair.gate-advanced":
38441
38648
  yield* sql`
@@ -39755,12 +39962,12 @@ const runProcessCore = Effect.fn("processRunner.runProcessCore")(function* (spaw
39755
39962
  stderrInvalidUtf8: stderr.invalidUtf8
39756
39963
  };
39757
39964
  });
39758
- const make$96 = Effect.fn("ProcessRunner.make")(function* () {
39965
+ const make$97 = Effect.fn("ProcessRunner.make")(function* () {
39759
39966
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
39760
39967
  const run = (input) => finalizeRunProcess(runProcessCore(spawner, input), input);
39761
39968
  return ProcessRunner.of({ run });
39762
39969
  });
39763
- const layer$81 = Layer.effect(ProcessRunner, make$96());
39970
+ const layer$82 = Layer.effect(ProcessRunner, make$97());
39764
39971
  //#endregion
39765
39972
  //#region src/project/RepositoryIdentityResolver.ts
39766
39973
  const DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY = 512;
@@ -39851,7 +40058,7 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("RepositoryIdentityResol
39851
40058
  rootPath: cacheKey
39852
40059
  }) : null;
39853
40060
  });
39854
- const make$95 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
40061
+ const make$96 = Effect.fn("RepositoryIdentityResolver.make")(function* (options = {}) {
39855
40062
  const processRunner = yield* ProcessRunner;
39856
40063
  const repositoryIdentityCache = yield* Cache.makeWith((cacheKey) => resolveRepositoryIdentityFromCacheKey(cacheKey).pipe(Effect.provideService(ProcessRunner, processRunner)), {
39857
40064
  capacity: options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY,
@@ -39866,7 +40073,7 @@ const make$95 = Effect.fn("RepositoryIdentityResolver.make")(function* (options
39866
40073
  });
39867
40074
  return RepositoryIdentityResolver.of({ resolve });
39868
40075
  });
39869
- const layer$80 = Layer.effect(RepositoryIdentityResolver, make$95()).pipe(Layer.provide(layer$81));
40076
+ const layer$81 = Layer.effect(RepositoryIdentityResolver, make$96()).pipe(Layer.provide(layer$82));
39870
40077
  //#endregion
39871
40078
  //#region src/orchestration/Layers/ProjectionSnapshotQuery.ts
39872
40079
  const decodeReadModel = Schema$1.decodeUnknownEffect(OrchestrationReadModel);
@@ -39923,6 +40130,7 @@ const ProjectionThreadPairDbRowSchema = Schema$1.Struct({
39923
40130
  activationTurnId: Schema$1.NullOr(TurnId),
39924
40131
  roundCap: NonNegativeInt,
39925
40132
  gateTimeoutMs: NonNegativeInt,
40133
+ reviewExperiment: Schema$1.NullOr(Schema$1.fromJsonString(FusionReviewExperiment)),
39926
40134
  activeGate: Schema$1.NullOr(Schema$1.fromJsonString(OrchestrationThreadPairGate)),
39927
40135
  createdAt: IsoDateTime,
39928
40136
  detachedAt: Schema$1.NullOr(IsoDateTime)
@@ -40175,6 +40383,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
40175
40383
  activation_turn_id AS "activationTurnId",
40176
40384
  round_cap AS "roundCap",
40177
40385
  gate_timeout_ms AS "gateTimeoutMs",
40386
+ review_experiment_json AS "reviewExperiment",
40178
40387
  active_gate_json AS "activeGate",
40179
40388
  created_at AS "createdAt",
40180
40389
  detached_at AS "detachedAt"
@@ -40194,6 +40403,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
40194
40403
  activation_turn_id AS "activationTurnId",
40195
40404
  round_cap AS "roundCap",
40196
40405
  gate_timeout_ms AS "gateTimeoutMs",
40406
+ review_experiment_json AS "reviewExperiment",
40197
40407
  active_gate_json AS "activeGate",
40198
40408
  created_at AS "createdAt",
40199
40409
  detached_at AS "detachedAt"
@@ -41541,6 +41751,72 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
41541
41751
  if (isPersistenceError(error)) return error;
41542
41752
  return toPersistenceSqlError("ProjectionSnapshotQuery.getSnapshot:query")(error);
41543
41753
  }));
41754
+ const readHandoffThread = SqlSchema.findOneOption({
41755
+ Request: ThreadIdLookupInput,
41756
+ Result: ProjectionBtwThreadRowSchema,
41757
+ execute: ({ threadId }) => sql`
41758
+ SELECT threads.project_id AS "projectId",
41759
+ COALESCE(projects.workspace_root, threads.worktree_path, '') AS cwd
41760
+ FROM projection_threads AS threads
41761
+ LEFT JOIN projection_projects AS projects ON projects.project_id = threads.project_id
41762
+ WHERE threads.thread_id = ${threadId} AND threads.deleted_at IS NULL
41763
+ AND (threads.project_id = ${P4_CHAT_PROJECT_ID} OR projects.deleted_at IS NULL)
41764
+ `
41765
+ });
41766
+ const readHandoffRows = SqlSchema.findAll({
41767
+ Request: ThreadIdLookupInput,
41768
+ Result: Schema$1.Struct({
41769
+ threadId: ThreadId,
41770
+ role: Schema$1.String,
41771
+ text: Schema$1.String
41772
+ }),
41773
+ execute: ({ threadId }) => sql`
41774
+ WITH latest_pair AS (
41775
+ SELECT implementer_thread_id, watcher_thread_id FROM thread_pairs
41776
+ WHERE implementer_thread_id = ${threadId} OR watcher_thread_id = ${threadId}
41777
+ ORDER BY created_at DESC LIMIT 1
41778
+ ), sources AS (
41779
+ SELECT ${threadId} AS id
41780
+ UNION SELECT implementer_thread_id FROM latest_pair
41781
+ UNION SELECT watcher_thread_id FROM latest_pair
41782
+ ), ranked AS (
41783
+ SELECT messages.thread_id AS "threadId", messages.role,
41784
+ substr(messages.text, -${BTW_CONTEXT_CHARACTER_LIMIT}) AS text,
41785
+ messages.created_at, messages.message_id,
41786
+ ROW_NUMBER() OVER (PARTITION BY messages.thread_id ORDER BY messages.created_at DESC, messages.message_id DESC) AS recent,
41787
+ ROW_NUMBER() OVER (PARTITION BY messages.thread_id ORDER BY messages.created_at, messages.message_id) AS earliest
41788
+ FROM projection_thread_messages AS messages
41789
+ JOIN projection_threads AS threads ON threads.thread_id = messages.thread_id
41790
+ WHERE messages.thread_id IN (SELECT id FROM sources)
41791
+ AND threads.deleted_at IS NULL AND messages.is_streaming = 0
41792
+ AND messages.role IN ('user', 'assistant')
41793
+ ) SELECT "threadId", role, text FROM ranked
41794
+ WHERE recent <= ${BTW_CONTEXT_MESSAGE_LIMIT} OR earliest = 1
41795
+ ORDER BY created_at, message_id
41796
+ `
41797
+ });
41798
+ const getHandoffContext = Effect.fn("ProjectionSnapshotQuery.getHandoffContext")(function* (threadId) {
41799
+ return yield* sql.withTransaction(Effect.gen(function* () {
41800
+ const thread = yield* readHandoffThread({ threadId });
41801
+ if (Option.isNone(thread)) return Option.none();
41802
+ const rows = yield* readHandoffRows({ threadId });
41803
+ const sourceIds = [...new Set(rows.map((row) => row.threadId))];
41804
+ const sourceCount = Math.max(1, sourceIds.length);
41805
+ const budget = Math.floor((BTW_CONTEXT_CHARACTER_LIMIT - 2 * (sourceCount - 1)) / sourceCount);
41806
+ const transcript = sourceIds.map((sourceId) => {
41807
+ const text = rows.filter((row) => row.threadId === sourceId).map((row) => `${row.role}: ${row.text}`).join("\n\n");
41808
+ const heading = `Thread ${sourceId} (bounded history):\n`;
41809
+ const available = budget - heading.length;
41810
+ const omission = "\n[Earlier history omitted]\n";
41811
+ const beginning = Math.floor(available / 4);
41812
+ return heading + (text.length <= available ? text : text.slice(0, beginning) + omission + text.slice(-(available - beginning - 27)));
41813
+ }).join("\n\n");
41814
+ return Option.some({
41815
+ ...thread.value,
41816
+ transcript
41817
+ });
41818
+ })).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getHandoffContext:query", "ProjectionSnapshotQuery.getHandoffContext:decodeRows")));
41819
+ });
41544
41820
  const getBtwContext = Effect.fn("ProjectionSnapshotQuery.getBtwContext")(function* (threadId) {
41545
41821
  const [thread, rows] = yield* Effect.all([getBtwThreadRow({ threadId }), listBtwContextRows({ threadId })]).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getBtwContext:query", "ProjectionSnapshotQuery.getBtwContext:decodeRows")));
41546
41822
  if (Option.isNone(thread)) return Option.none();
@@ -42124,11 +42400,12 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
42124
42400
  getThreadPairById,
42125
42401
  getThreadShellById,
42126
42402
  getThreadDetailById,
42403
+ getHandoffContext,
42127
42404
  getBtwContext,
42128
42405
  getThreadDetailSnapshot
42129
42406
  };
42130
42407
  });
42131
- const OrchestrationProjectionSnapshotQueryLive = Layer.effect(ProjectionSnapshotQuery, makeProjectionSnapshotQuery).pipe(Layer.provide(layer$82));
42408
+ const OrchestrationProjectionSnapshotQueryLive = Layer.effect(ProjectionSnapshotQuery, makeProjectionSnapshotQuery).pipe(Layer.provide(layer$83));
42132
42409
  //#endregion
42133
42410
  //#region src/orchestration/Services/ThreadEventStream.ts
42134
42411
  var ThreadEventStreamService = class extends Context.Service()("@p4code/cli/orchestration/Services/ThreadEventStream/ThreadEventStreamService") {};
@@ -42398,7 +42675,7 @@ const OrchestrationProjectionPipelineLayerLive = OrchestrationProjectionPipeline
42398
42675
  * ingestion writes it, and layers memoize by reference, so both must receive
42399
42676
  * THIS layer or they would each get their own empty registry.
42400
42677
  */
42401
- const ThreadBackgroundLivenessLayerLive = layer$82;
42678
+ const ThreadBackgroundLivenessLayerLive = layer$83;
42402
42679
  const OrchestrationInfrastructureLayerLive = Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive, ThreadBackgroundLivenessLayerLive);
42403
42680
  const OrchestrationEngineLayerLive = OrchestrationEngineLive.pipe(Layer.provide(OrchestrationInfrastructureLayerLive));
42404
42681
  const OrchestrationLayerLive = Layer.mergeAll(OrchestrationInfrastructureLayerLive, OrchestrationEngineLayerLive, ThreadEventStreamLive.pipe(Layer.provide(OrchestrationEngineLayerLive), Layer.provide(OrchestrationEventStoreLive)));
@@ -42873,7 +43150,7 @@ function mergeWithDefaultKeybindings(custom) {
42873
43150
  * Keybindings - Service tag for keybinding configuration operations.
42874
43151
  */
42875
43152
  var Keybindings = class extends Context.Service()("@p4code/cli/keybindings") {};
42876
- const make$94 = Effect.gen(function* () {
43153
+ const make$95 = Effect.gen(function* () {
42877
43154
  const { keybindingsConfigPath } = yield* ServerConfig$1;
42878
43155
  const fs = yield* FileSystem.FileSystem;
42879
43156
  const path = yield* Path$1.Path;
@@ -43134,7 +43411,7 @@ const make$94 = Effect.gen(function* () {
43134
43411
  }))
43135
43412
  };
43136
43413
  });
43137
- const layer$79 = Layer.effect(Keybindings, make$94);
43414
+ const layer$80 = Layer.effect(Keybindings, make$95);
43138
43415
  //#endregion
43139
43416
  //#region src/process/externalLauncher.ts
43140
43417
  /**
@@ -43361,7 +43638,7 @@ const launchEditorProcess = Effect.fn("externalLauncher.launchEditorProcess")(fu
43361
43638
  cause
43362
43639
  }));
43363
43640
  });
43364
- const make$93 = Effect.gen(function* () {
43641
+ const make$94 = Effect.gen(function* () {
43365
43642
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
43366
43643
  const fileSystem = yield* FileSystem.FileSystem;
43367
43644
  const path = yield* Path$1.Path;
@@ -43372,7 +43649,7 @@ const make$93 = Effect.gen(function* () {
43372
43649
  launchEditor: (input) => provideCommandResolutionServices(Effect.flatMap(resolveEditorLaunch(input), (launch) => launchEditorProcess(launch).pipe(Effect.provideService(ChildProcessSpawner$1.ChildProcessSpawner, spawner))))
43373
43650
  });
43374
43651
  });
43375
- const layer$78 = Layer.effect(ExternalLauncher, make$93);
43652
+ const layer$79 = Layer.effect(ExternalLauncher, make$94);
43376
43653
  //#endregion
43377
43654
  //#region src/orchestration/Services/OrchestrationReactor.ts
43378
43655
  /**
@@ -43390,7 +43667,7 @@ var OrchestrationReactor = class extends Context.Service()("@p4code/cli/orchestr
43390
43667
  //#endregion
43391
43668
  //#region src/serverLifecycleEvents.ts
43392
43669
  var ServerLifecycleEvents = class extends Context.Service()("@p4code/cli/serverLifecycleEvents") {};
43393
- const make$92 = Effect.gen(function* () {
43670
+ const make$93 = Effect.gen(function* () {
43394
43671
  const pubsub = yield* PubSub.unbounded();
43395
43672
  const state = yield* Ref.make({
43396
43673
  sequence: 0,
@@ -43414,7 +43691,7 @@ const make$92 = Effect.gen(function* () {
43414
43691
  }
43415
43692
  };
43416
43693
  });
43417
- const layer$77 = Layer.effect(ServerLifecycleEvents, make$92);
43694
+ const layer$78 = Layer.effect(ServerLifecycleEvents, make$93);
43418
43695
  //#endregion
43419
43696
  //#region src/telemetry/Identify.ts
43420
43697
  const CodexAuthJsonSchema = Schema$1.Struct({ tokens: Schema$1.Struct({ account_id: Schema$1.String }) });
@@ -43587,7 +43864,7 @@ var AnalyticsService = class AnalyticsService extends Context.Service()("@p4code
43587
43864
  /** No-op layer for callers that intentionally disable telemetry. */
43588
43865
  static layerTest = Layer.succeed(AnalyticsService, inert);
43589
43866
  };
43590
- const make$91 = Effect.gen(function* () {
43867
+ const make$92 = Effect.gen(function* () {
43591
43868
  const telemetryConfig = yield* TelemetryEnvConfig;
43592
43869
  const posthogKey = telemetryConfig.posthogKey.trim();
43593
43870
  if (!telemetryConfig.enabled || posthogKey === "") return inert;
@@ -43657,7 +43934,7 @@ const make$91 = Effect.gen(function* () {
43657
43934
  flush
43658
43935
  });
43659
43936
  });
43660
- const layer$76 = Layer.effect(AnalyticsService, make$91);
43937
+ const layer$77 = Layer.effect(AnalyticsService, make$92);
43661
43938
  AnalyticsService.layerTest;
43662
43939
  //#endregion
43663
43940
  //#region src/provider/Services/ProviderSessionDirectory.ts
@@ -44088,7 +44365,7 @@ var BootServiceInstallError = class extends Schema$1.TaggedErrorClass()("BootSer
44088
44365
  }
44089
44366
  };
44090
44367
  var BootService = class extends Context.Service()("@p4code/cli/service/bootService") {};
44091
- const make$90 = Effect.fn("cloud.boot_service.make")(function* (input) {
44368
+ const make$91 = Effect.fn("cloud.boot_service.make")(function* (input) {
44092
44369
  const hostExecPath = yield* HostProcessExecutablePath;
44093
44370
  const hostArguments = yield* HostProcessArguments;
44094
44371
  const host = input.host ?? {
@@ -44310,7 +44587,7 @@ const make$90 = Effect.fn("cloud.boot_service.make")(function* (input) {
44310
44587
  logPath
44311
44588
  });
44312
44589
  });
44313
- const layer$75 = (input) => Layer.effect(BootService, make$90(input));
44590
+ const layer$76 = (input) => Layer.effect(BootService, make$91(input));
44314
44591
  //#endregion
44315
44592
  //#region src/service/selfUpdate.ts
44316
44593
  /**
@@ -44385,7 +44662,7 @@ const resolveServerSelfUpdateCapability = Effect.fn("cloud.server_self_update.re
44385
44662
  return null;
44386
44663
  });
44387
44664
  var ServerSelfUpdate = class extends Context.Service()("@p4code/cli/service/selfUpdate/ServerSelfUpdate") {};
44388
- const make$89 = Effect.fn("cloud.server_self_update.make")(function* (options) {
44665
+ const make$90 = Effect.fn("cloud.server_self_update.make")(function* (options) {
44389
44666
  const serverConfig = yield* ServerConfig$1;
44390
44667
  const settings = yield* ServerSettingsService;
44391
44668
  const continuationContext = yield* Effect.context();
@@ -44548,7 +44825,7 @@ const make$89 = Effect.fn("cloud.server_self_update.make")(function* (options) {
44548
44825
  });
44549
44826
  return ServerSelfUpdate.of({ update });
44550
44827
  });
44551
- const layer$74 = Layer.effect(ServerSelfUpdate, make$89()).pipe(Layer.provide(layer$81));
44828
+ const layer$75 = Layer.effect(ServerSelfUpdate, make$90()).pipe(Layer.provide(layer$82));
44552
44829
  //#endregion
44553
44830
  //#region src/environment/ServerEnvironmentLabel.ts
44554
44831
  const ServerEnvironmentLabelCommandProbe = Schema$1.Literals(["macos-computer-name", "linux-pretty-hostname"]);
@@ -44680,7 +44957,7 @@ function platformArch(architecture) {
44680
44957
  default: return "other";
44681
44958
  }
44682
44959
  }
44683
- const make$88 = Effect.gen(function* () {
44960
+ const make$89 = Effect.gen(function* () {
44684
44961
  const fileSystem = yield* FileSystem.FileSystem;
44685
44962
  const path = yield* Path$1.Path;
44686
44963
  const serverConfig = yield* ServerConfig$1;
@@ -44758,7 +45035,7 @@ const make$88 = Effect.gen(function* () {
44758
45035
  * state. It intentionally has no fallback Layer.succeed value: callers must
44759
45036
  * provide the external platform services and a ServerConfig.
44760
45037
  */
44761
- const layer$73 = Layer.effect(ServerEnvironment, make$88).pipe(Layer.provide(layer$81));
45038
+ const layer$74 = Layer.effect(ServerEnvironment, make$89).pipe(Layer.provide(layer$82));
44762
45039
  //#endregion
44763
45040
  //#region src/provider/Services/ProviderSessionReaper.ts
44764
45041
  var ProviderSessionReaper = class extends Context.Service()("@p4code/cli/provider/Services/ProviderSessionReaper") {};
@@ -45113,7 +45390,7 @@ function daemonPortLine(line) {
45113
45390
  const port = Number(raw);
45114
45391
  return Number.isInteger(port) && port > 0 && port <= MAX_PORT$1 ? port : null;
45115
45392
  }
45116
- const make$87 = Effect.fn("AgentDeviceProcess.make")(function* () {
45393
+ const make$88 = Effect.fn("AgentDeviceProcess.make")(function* () {
45117
45394
  const fs = yield* FileSystem.FileSystem;
45118
45395
  const path = yield* Path$1.Path;
45119
45396
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
@@ -45225,14 +45502,14 @@ const make$87 = Effect.fn("AgentDeviceProcess.make")(function* () {
45225
45502
  });
45226
45503
  return { start };
45227
45504
  });
45228
- const layer$72 = Layer.effect(AgentDeviceProcess, make$87());
45505
+ const layer$73 = Layer.effect(AgentDeviceProcess, make$88());
45229
45506
  //#endregion
45230
45507
  //#region src/device/DeviceDiscovery.ts
45231
45508
  var DeviceDiscoveryFailure = class extends Data.TaggedError("DeviceDiscoveryFailure") {};
45232
45509
  const MAX_DEVICES = 512;
45233
45510
  const MAX_RESPONSE_BYTES$6 = 1024 * 1024;
45234
45511
  const MAX_DEVICE_LABEL = 512;
45235
- const REQUEST_TIMEOUT_MS$5 = 15e3;
45512
+ const REQUEST_TIMEOUT_MS$6 = 15e3;
45236
45513
  const HubDevice = Schema$1.Struct({
45237
45514
  id: DeviceId,
45238
45515
  name: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_DEVICE_LABEL)),
@@ -45258,7 +45535,7 @@ const decodeDeviceDiscovery = (hostId, body) => Schema$1.decodeUnknownEffect(Sch
45258
45535
  partial: (value.errors?.length ?? 0) > 0
45259
45536
  });
45260
45537
  }));
45261
- const make$86 = Effect.fn("DeviceDiscovery.make")(function* () {
45538
+ const make$87 = Effect.fn("DeviceDiscovery.make")(function* () {
45262
45539
  const http = yield* HttpClient.HttpClient;
45263
45540
  return Effect.fn("DeviceDiscovery.read")(function* (input) {
45264
45541
  const origin = yield* Effect.try(() => new URL(input.ready.hub.origin));
@@ -45275,7 +45552,10 @@ const make$86 = Effect.fn("DeviceDiscovery.make")(function* () {
45275
45552
  });
45276
45553
  if (body.invalidUtf8 || body.truncated) return yield* new DeviceDiscoveryFailure({ message: "Invalid device discovery response." });
45277
45554
  const result = yield* decodeDeviceDiscovery(input.hostId, body.text);
45278
- const devices = [...result.devices];
45555
+ const adapterDevices = input.ready.xcode ? yield* input.ready.xcode.discover : [];
45556
+ const simulatorIds = new Set(adapterDevices.map((device) => device.id));
45557
+ const devices = [...result.devices.filter((device) => device.platform !== "ios" || device.physical || !simulatorIds.has(device.id)), ...adapterDevices];
45558
+ if (devices.length > MAX_DEVICES || new Set(devices.map((device) => device.id)).size !== devices.length || adapterDevices.some((device) => device.hostId !== input.hostId || device.platform !== "ios" || device.physical)) return yield* new DeviceDiscoveryFailure({ message: "Invalid Xcode simulator identity." });
45279
45559
  if (input.androidAvailable) {
45280
45560
  const avds = yield* input.ready.run("emulator", ["-list-avds"]);
45281
45561
  if (avds.code !== 0) return yield* new DeviceDiscoveryFailure({ message: "Android virtual device discovery failed." });
@@ -45299,7 +45579,7 @@ const make$86 = Effect.fn("DeviceDiscovery.make")(function* () {
45299
45579
  devices,
45300
45580
  partial: result.partial
45301
45581
  };
45302
- }, Effect.scoped, Effect.timeout(Duration.millis(REQUEST_TIMEOUT_MS$5)), Effect.mapError(() => new DeviceOperationError({
45582
+ }, Effect.scoped, Effect.timeout(Duration.millis(REQUEST_TIMEOUT_MS$6)), Effect.mapError(() => new DeviceOperationError({
45303
45583
  operation: "list",
45304
45584
  reason: "request_failed",
45305
45585
  cause: "Device discovery unavailable."
@@ -45317,7 +45597,7 @@ const Result = Schema$1.Struct({
45317
45597
  serial: Schema$1.optional(DeviceId),
45318
45598
  error: Schema$1.optional(Schema$1.String)
45319
45599
  });
45320
- const make$85 = Effect.fn("DeviceHubActions.make")(function* () {
45600
+ const make$86 = Effect.fn("DeviceHubActions.make")(function* () {
45321
45601
  const http = yield* HttpClient.HttpClient;
45322
45602
  const post = Effect.fn("DeviceHubActions.post")(function* (ready, route, payload, timeoutMs) {
45323
45603
  return yield* Effect.gen(function* () {
@@ -45386,7 +45666,7 @@ function sessionsToClose(sessions, input) {
45386
45666
  //#endregion
45387
45667
  //#region src/device/DeviceCommandLeases.ts
45388
45668
  /** Owns commands by exact session object, so close/reopen cannot revive an old lease. */
45389
- const make$84 = Effect.fn("DeviceCommandLeases.make")(function* () {
45669
+ const make$85 = Effect.fn("DeviceCommandLeases.make")(function* () {
45390
45670
  const lock = yield* Semaphore.make(1);
45391
45671
  const entries = /* @__PURE__ */ new Map();
45392
45672
  const revoked = /* @__PURE__ */ new WeakSet();
@@ -45432,7 +45712,7 @@ const make$84 = Effect.fn("DeviceCommandLeases.make")(function* () {
45432
45712
  //#endregion
45433
45713
  //#region src/device/DeviceThreadLeases.ts
45434
45714
  /** Thread retirement also cancels opens that have not created a session yet. */
45435
- const make$83 = Effect.fn("DeviceThreadLeases.make")(function* () {
45715
+ const make$84 = Effect.fn("DeviceThreadLeases.make")(function* () {
45436
45716
  const lock = yield* Semaphore.make(1);
45437
45717
  const scopes = /* @__PURE__ */ new Map();
45438
45718
  const blocked = /* @__PURE__ */ new Set();
@@ -45480,7 +45760,7 @@ const make$83 = Effect.fn("DeviceThreadLeases.make")(function* () {
45480
45760
  //#region src/device/DeviceHostRegistry.ts
45481
45761
  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;
45482
45762
  /** Owns configuration generations; use() interrupts in-flight work before a host is retired. */
45483
- const make$82 = Effect.fn("DeviceHostRegistry.make")(function* (factories) {
45763
+ const make$83 = Effect.fn("DeviceHostRegistry.make")(function* (factories) {
45484
45764
  const factoryContext = yield* Effect.context();
45485
45765
  const lock = yield* Semaphore.make(1);
45486
45766
  const entries = /* @__PURE__ */ new Map();
@@ -45603,6 +45883,675 @@ const start = Effect.fn("DeviceHostSettings.start")(function* (input) {
45603
45883
  return { refresh };
45604
45884
  });
45605
45885
  //#endregion
45886
+ //#region src/device/DeviceScreenshot.ts
45887
+ const MAX_SCREENSHOT_BYTES = 8 * 1024 * 1024;
45888
+ var DeviceScreenshotFailure = class extends Data.TaggedError("DeviceScreenshotFailure") {};
45889
+ const MAX_DIMENSION = 16384;
45890
+ const MAX_PIXELS = 64 * 1024 * 1024;
45891
+ const SCREENSHOT_TIMEOUT_MS = 2e4;
45892
+ const PNG_HEADER_BYTES = 33;
45893
+ const PNG_CHUNK_OVERHEAD = 12;
45894
+ const PNG_SIGNATURE = Buffer.from([
45895
+ 137,
45896
+ 80,
45897
+ 78,
45898
+ 71,
45899
+ 13,
45900
+ 10,
45901
+ 26,
45902
+ 10
45903
+ ]);
45904
+ function pngDimensions(png) {
45905
+ const buffer = Buffer.from(png.buffer, png.byteOffset, png.byteLength);
45906
+ if (buffer.length < PNG_HEADER_BYTES || buffer.length > 8388608 || !buffer.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE) || buffer.readUInt32BE(8) !== 13 || buffer.toString("ascii", 12, 16) !== "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG screenshot." });
45907
+ const width = buffer.readUInt32BE(16);
45908
+ const height = buffer.readUInt32BE(20);
45909
+ if (!width || !height || width > MAX_DIMENSION || height > MAX_DIMENSION || width * height > MAX_PIXELS) throw new DeviceScreenshotFailure({ message: "Screenshot dimensions exceed limits." });
45910
+ let offset = PNG_HEADER_BYTES;
45911
+ let imageData = false;
45912
+ let ended = false;
45913
+ while (offset + PNG_CHUNK_OVERHEAD <= buffer.length) {
45914
+ const length = buffer.readUInt32BE(offset);
45915
+ const type = buffer.toString("ascii", offset + 4, offset + 8);
45916
+ const next = offset + PNG_CHUNK_OVERHEAD + length;
45917
+ if (next > buffer.length || type === "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG chunk." });
45918
+ if (type === "IDAT") imageData = true;
45919
+ if (type === "IEND") {
45920
+ ended = length === 0 && next === buffer.length;
45921
+ break;
45922
+ }
45923
+ offset = next;
45924
+ }
45925
+ if (!imageData || !ended) throw new DeviceScreenshotFailure({ message: "Incomplete PNG screenshot." });
45926
+ return {
45927
+ width,
45928
+ height
45929
+ };
45930
+ }
45931
+ const make$82 = Effect.fn("DeviceScreenshot.make")(function* () {
45932
+ const http = yield* HttpClient.HttpClient;
45933
+ return Effect.fn("DeviceScreenshot.capture")(function* (ready, session) {
45934
+ const origin = yield* Effect.try(() => new URL(ready.hub.origin));
45935
+ if (origin.protocol !== "http:" || origin.hostname !== "127.0.0.1" || !origin.port || origin.username || origin.password || origin.pathname !== "/" || origin.search || origin.hash) return yield* new DeviceScreenshotFailure({ message: "Invalid owned hub." });
45936
+ const vendor = session.platform === "ios" ? "serve-sim" : "serve-emu";
45937
+ const response = yield* http.execute(HttpClientRequest.post(`${origin.origin}/vendor/${vendor}/api/screenshot?device=${encodeURIComponent(session.deviceId)}`));
45938
+ if (response.status !== 200) return yield* new DeviceScreenshotFailure({ message: "Screenshot rejected." });
45939
+ let bytes = 0;
45940
+ const chunks = [];
45941
+ yield* response.stream.pipe(Stream.runForEach((chunk) => Effect.gen(function* () {
45942
+ bytes += chunk.byteLength;
45943
+ if (bytes > 8388608) return yield* new DeviceScreenshotFailure({ message: "Screenshot too large." });
45944
+ chunks.push(chunk);
45945
+ })));
45946
+ const png = Buffer.concat(chunks, bytes);
45947
+ return {
45948
+ png,
45949
+ ...yield* Effect.try(() => pngDimensions(png))
45950
+ };
45951
+ }, Effect.scoped, Effect.timeout(Duration.millis(SCREENSHOT_TIMEOUT_MS)), Effect.mapError(() => new DeviceOperationError({
45952
+ operation: "screenshot",
45953
+ reason: "request_failed",
45954
+ cause: "Device screenshot could not be captured."
45955
+ })));
45956
+ });
45957
+ var DeviceScreenshot = class extends Context.Service()("@p4code/cli/device/DeviceScreenshot") {};
45958
+ const layer$72 = Layer.effect(DeviceScreenshot, make$82().pipe(Effect.map((capture) => ({ capture }))));
45959
+ //#endregion
45960
+ //#region src/device/XcodeMcpClient.ts
45961
+ const XCODE_BUILD_MCP_VERSION = "2.7.0";
45962
+ const PROTOCOL_VERSION = "2025-06-18";
45963
+ const REQUEST_TIMEOUT_MS$5 = 6e4;
45964
+ const MAX_MESSAGE_BYTES = 12 * 1024 * 1024;
45965
+ const MAX_TOOL_PAGES = 32;
45966
+ function objectValue(value) {
45967
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
45968
+ }
45969
+ /** One process per lease keeps semantic refs and simulator defaults isolated. Never invokes npx. */
45970
+ var XcodeMcpClient = class XcodeMcpClient {
45971
+ child;
45972
+ sequence = 0;
45973
+ buffer = "";
45974
+ closed = false;
45975
+ pending = /* @__PURE__ */ new Map();
45976
+ tools = /* @__PURE__ */ new Set();
45977
+ constructor(child) {
45978
+ this.child = child;
45979
+ child.stdout.setEncoding("utf8");
45980
+ child.stdout.on("data", (chunk) => {
45981
+ this.buffer += chunk;
45982
+ if (Buffer.byteLength(this.buffer) > MAX_MESSAGE_BYTES) {
45983
+ this.close(/* @__PURE__ */ new Error("Device MCP response exceeds the supported size."));
45984
+ return;
45985
+ }
45986
+ let boundary;
45987
+ while ((boundary = this.buffer.indexOf("\n")) >= 0) {
45988
+ const line = this.buffer.slice(0, boundary);
45989
+ this.buffer = this.buffer.slice(boundary + 1);
45990
+ try {
45991
+ const message = objectValue(JSON.parse(line));
45992
+ if (typeof message.id !== "number") continue;
45993
+ const call = this.pending.get(message.id);
45994
+ if (!call) continue;
45995
+ this.pending.delete(message.id);
45996
+ if (message.error) {
45997
+ const detail = objectValue(message.error).message;
45998
+ call.reject(new Error(typeof detail === "string" ? detail : "Device MCP request failed."));
45999
+ } else call.resolve(message.result);
46000
+ } catch {
46001
+ this.close(/* @__PURE__ */ new Error("Device MCP returned invalid stdio JSON."));
46002
+ return;
46003
+ }
46004
+ }
46005
+ });
46006
+ child.stderr.resume();
46007
+ child.on("error", () => this.close(/* @__PURE__ */ new Error("Device MCP executable could not start.")));
46008
+ child.on("exit", () => this.close(/* @__PURE__ */ new Error("Device MCP disconnected. Select the device again.")));
46009
+ child.stdin.on("error", () => this.close(/* @__PURE__ */ new Error("Device MCP input disconnected.")));
46010
+ }
46011
+ static async start(executable, cwd, environment, signal, kind = "legacy") {
46012
+ if (!isAbsolute(executable)) throw new Error("Device MCP requires an absolute executable path.");
46013
+ await access(executable, constants.X_OK).catch(() => {
46014
+ throw new Error(kind === "apple" ? "Selected Xcode bridge is missing or not executable. Check the selected Xcode installation." : "Configured XcodeBuildMCP executable is missing or not executable. Install 2.7.0 explicitly.");
46015
+ });
46016
+ signal.throwIfAborted();
46017
+ const client = new XcodeMcpClient(spawn(executable, kind === "apple" ? ["mcpbridge"] : ["mcp"], {
46018
+ cwd,
46019
+ shell: false,
46020
+ stdio: "pipe",
46021
+ env: {
46022
+ ...environment,
46023
+ ...kind === "legacy" ? {
46024
+ XCODEBUILDMCP_CWD: cwd,
46025
+ XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS: "true",
46026
+ XCODEBUILDMCP_ENABLED_WORKFLOWS: "simulator,ui-automation",
46027
+ XCODEBUILDMCP_SENTRY_DISABLED: "true"
46028
+ } : {}
46029
+ }
46030
+ }));
46031
+ try {
46032
+ const initialized = objectValue(await client.request("initialize", {
46033
+ protocolVersion: PROTOCOL_VERSION,
46034
+ capabilities: {},
46035
+ clientInfo: {
46036
+ name: "p4code-device-adapter",
46037
+ version: "1"
46038
+ }
46039
+ }, signal));
46040
+ if (kind === "apple" && objectValue(initialized.serverInfo).name !== "xcode-tools") throw new Error("Selected Xcode did not expose Apple MCP tools.");
46041
+ if (kind === "legacy" && objectValue(initialized.serverInfo).version !== "2.7.0") throw new Error(`P4 requires XcodeBuildMCP ${XCODE_BUILD_MCP_VERSION}; configured executable reported a different version.`);
46042
+ client.child.stdin.write(JSON.stringify({
46043
+ jsonrpc: "2.0",
46044
+ method: "notifications/initialized"
46045
+ }) + "\n");
46046
+ let cursor;
46047
+ const cursors = /* @__PURE__ */ new Set();
46048
+ do {
46049
+ const page = objectValue(await client.request("tools/list", cursor ? { cursor } : {}, signal));
46050
+ if (!Array.isArray(page.tools)) throw new Error("Device MCP did not advertise tools.");
46051
+ for (const tool of page.tools) {
46052
+ const name = objectValue(tool).name;
46053
+ if (typeof name === "string") client.tools.add(name);
46054
+ }
46055
+ cursor = page.nextCursor;
46056
+ if (typeof cursor === "string" && cursor.length > 0) {
46057
+ if (cursors.has(cursor) || cursors.size >= MAX_TOOL_PAGES) throw new Error("Device MCP tool discovery did not finish within its page limit.");
46058
+ cursors.add(cursor);
46059
+ }
46060
+ } while (typeof cursor === "string" && cursor.length > 0);
46061
+ return client;
46062
+ } catch (error) {
46063
+ client.close();
46064
+ throw error;
46065
+ }
46066
+ }
46067
+ request(method, params, signal) {
46068
+ signal.throwIfAborted();
46069
+ if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("Device MCP session is closed. Select the device again."));
46070
+ const id = ++this.sequence;
46071
+ return new Promise((resolve, reject) => {
46072
+ const abort = () => this.close(/* @__PURE__ */ new Error("Device MCP request cancelled. Select the device again."));
46073
+ const timer = setTimeout(() => this.close(/* @__PURE__ */ new Error("Device MCP timed out. Select the device again.")), REQUEST_TIMEOUT_MS$5);
46074
+ const finish = () => {
46075
+ clearTimeout(timer);
46076
+ signal.removeEventListener("abort", abort);
46077
+ };
46078
+ this.pending.set(id, {
46079
+ resolve: (result) => {
46080
+ finish();
46081
+ resolve(result);
46082
+ },
46083
+ reject: (error) => {
46084
+ finish();
46085
+ reject(error);
46086
+ }
46087
+ });
46088
+ signal.addEventListener("abort", abort, { once: true });
46089
+ this.child.stdin.write(JSON.stringify({
46090
+ jsonrpc: "2.0",
46091
+ id,
46092
+ method,
46093
+ params
46094
+ }) + "\n");
46095
+ });
46096
+ }
46097
+ async call(name, args, signal) {
46098
+ if (!this.tools.has(name)) throw new Error(`Selected device MCP service does not expose ${name}. Check adapter capabilities and Xcode setup.`);
46099
+ const redact = (error) => {
46100
+ let message = error instanceof Error ? error.message : String(error);
46101
+ for (const [key, value] of Object.entries(args)) if (/sessionkey$/i.test(key) && typeof value === "string" && value.length > 0) message = message.replaceAll(value, "[redacted]");
46102
+ return new Error(message);
46103
+ };
46104
+ try {
46105
+ const result = objectValue(await this.request("tools/call", {
46106
+ name,
46107
+ arguments: args
46108
+ }, signal));
46109
+ const structured = objectValue(result.structuredContent);
46110
+ if (result.isError === true || structured.didError === true) {
46111
+ const detail = (Array.isArray(result.content) ? result.content : []).map((part) => objectValue(part).text).filter((part) => typeof part === "string").join("\n");
46112
+ throw new Error(detail || String(structured.error ?? `${name} failed. Refresh device state before retrying.`));
46113
+ }
46114
+ return result;
46115
+ } catch (error) {
46116
+ throw redact(error);
46117
+ }
46118
+ }
46119
+ close(error = /* @__PURE__ */ new Error("Device MCP session closed.")) {
46120
+ if (this.closed) return;
46121
+ this.closed = true;
46122
+ for (const call of this.pending.values()) call.reject(error);
46123
+ this.pending.clear();
46124
+ this.child.kill("SIGTERM");
46125
+ }
46126
+ };
46127
+ //#endregion
46128
+ //#region src/device/XcodeDeviceAdapter.ts
46129
+ const SimulatorList = Schema$1.Struct({ simulators: Schema$1.Array(Schema$1.Struct({
46130
+ simulatorId: Schema$1.String,
46131
+ name: Schema$1.String,
46132
+ runtime: Schema$1.String,
46133
+ state: Schema$1.String,
46134
+ isAvailable: Schema$1.Boolean
46135
+ })) });
46136
+ const decodeSimulators = Schema$1.decodeUnknownSync(SimulatorList);
46137
+ const MAX_LEASES = 32;
46138
+ const Frame = Schema$1.Struct({
46139
+ x: Schema$1.Number,
46140
+ y: Schema$1.Number,
46141
+ width: Schema$1.Number,
46142
+ height: Schema$1.Number
46143
+ });
46144
+ const Snapshot = Schema$1.Struct({ elements: Schema$1.Array(Schema$1.Struct({
46145
+ ref: Schema$1.String,
46146
+ role: Schema$1.optional(Schema$1.String),
46147
+ frame: Frame
46148
+ })) });
46149
+ const readSnapshot = Schema$1.decodeUnknownOption(Snapshot);
46150
+ /** Retain structured semantic snapshots; callers never need screenshot round trips for refs. */
46151
+ function xcodeResultData(result) {
46152
+ const structured = objectValue(result.structuredContent);
46153
+ return objectValue(structured.data ?? structured);
46154
+ }
46155
+ function createXcodeDeviceAdapter(executable, cwd, environment) {
46156
+ const clients = /* @__PURE__ */ new Map();
46157
+ const recordings = /* @__PURE__ */ new Map();
46158
+ const snapshots = /* @__PURE__ */ new Map();
46159
+ const remember = (session, data) => {
46160
+ const snapshot = readSnapshot(data.capture);
46161
+ if (snapshot._tag === "Some") snapshots.set(session.leaseId, snapshot.value);
46162
+ const viewport = snapshots.get(session.leaseId)?.elements.find((element) => element.role === "window")?.frame;
46163
+ return {
46164
+ ...data,
46165
+ ...viewport ? { viewport } : {}
46166
+ };
46167
+ };
46168
+ const get = async (key, signal) => {
46169
+ let pending = clients.get(key);
46170
+ if (!pending) {
46171
+ if (clients.size >= MAX_LEASES) throw new Error("Close an existing Xcode device session before opening another.");
46172
+ pending = XcodeMcpClient.start(executable, cwd, environment, signal);
46173
+ clients.set(key, pending);
46174
+ pending.catch(() => {
46175
+ if (clients.get(key) === pending) clients.delete(key);
46176
+ });
46177
+ }
46178
+ return pending;
46179
+ };
46180
+ const run = (operation, execute) => Effect.tryPromise({
46181
+ try: execute,
46182
+ catch: (cause) => new DeviceOperationError({
46183
+ operation,
46184
+ reason: "request_failed",
46185
+ cause: cause instanceof Error ? cause.message : String(cause)
46186
+ })
46187
+ });
46188
+ const forSession = async (session, signal) => {
46189
+ if (session.hostId !== "local" || session.platform !== "ios" || !session.leaseId) throw new Error("Xcode adapter requires an exact local iOS Simulator lease.");
46190
+ return get(session.leaseId, signal);
46191
+ };
46192
+ const closeSession = (leaseId) => {
46193
+ const client = clients.get(leaseId);
46194
+ clients.delete(leaseId);
46195
+ const recording = recordings.get(leaseId);
46196
+ recordings.delete(leaseId);
46197
+ snapshots.delete(leaseId);
46198
+ client?.then(async (client) => {
46199
+ try {
46200
+ if (recording) await client.call("record_sim_video", {
46201
+ ...recording,
46202
+ stop: true
46203
+ }, new AbortController().signal);
46204
+ } finally {
46205
+ client.close();
46206
+ }
46207
+ }).catch(() => {});
46208
+ };
46209
+ return {
46210
+ discover: run("list", async (signal) => {
46211
+ const client = await get("discovery", signal);
46212
+ return decodeSimulators(xcodeResultData(await client.call("list_sims", {}, signal))).simulators.filter((sim) => sim.isAvailable && /iOS/i.test(sim.runtime)).map((sim) => ({
46213
+ id: sim.simulatorId,
46214
+ hostId: "local",
46215
+ name: sim.name,
46216
+ version: sim.runtime,
46217
+ booted: sim.state === "Booted",
46218
+ physical: false,
46219
+ platform: "ios"
46220
+ }));
46221
+ }),
46222
+ state: (session, sinceScreenHash) => run("state", async (signal) => {
46223
+ const client = await forSession(session, signal);
46224
+ const data = xcodeResultData(await client.call("snapshot_ui", {
46225
+ simulatorId: session.deviceId,
46226
+ ...sinceScreenHash ? { sinceScreenHash } : {}
46227
+ }, signal));
46228
+ return {
46229
+ adapter: "xcodebuildmcp",
46230
+ capabilities: [...client.tools].filter((tool) => [
46231
+ "tap",
46232
+ "type_text",
46233
+ "swipe",
46234
+ "drag",
46235
+ "batch",
46236
+ "snapshot_ui",
46237
+ "screenshot",
46238
+ "record_sim_video"
46239
+ ].includes(tool)),
46240
+ ...remember(session, data)
46241
+ };
46242
+ }),
46243
+ action: (session, input) => run("command", async (signal) => {
46244
+ const client = await forSession(session, signal);
46245
+ const { action, ...parameters } = input;
46246
+ const snapshot = snapshots.get(session.leaseId);
46247
+ const ref = input.action === "batch" ? input.steps.at(-1)?.elementRef : input.action === "swipe" ? input.withinElementRef : input.elementRef;
46248
+ const bounds = snapshot?.elements.find((element) => element.ref === ref)?.frame;
46249
+ const viewport = snapshot?.elements.find((element) => element.role === "window")?.frame;
46250
+ const data = xcodeResultData(await client.call(action, {
46251
+ ...parameters,
46252
+ simulatorId: session.deviceId
46253
+ }, signal));
46254
+ return {
46255
+ ...remember(session, data),
46256
+ ...bounds && viewport && viewport.width > 0 && viewport.height > 0 ? { pointer: {
46257
+ x: (bounds.x + bounds.width / 2 - viewport.x) / viewport.width,
46258
+ y: (bounds.y + bounds.height / 2 - viewport.y) / viewport.height
46259
+ } } : {}
46260
+ };
46261
+ }),
46262
+ screenshot: (session) => run("screenshot", async (signal) => {
46263
+ const result = await (await forSession(session, signal)).call("screenshot", {
46264
+ simulatorId: session.deviceId,
46265
+ returnFormat: "base64"
46266
+ }, signal);
46267
+ const image = (Array.isArray(result.content) ? result.content : []).map(objectValue).find((part) => part.type === "image" && part.mimeType === "image/png");
46268
+ if (typeof image?.data !== "string") throw new Error("XcodeBuildMCP did not return a PNG screenshot.");
46269
+ const png = Buffer.from(image.data, "base64");
46270
+ return {
46271
+ png,
46272
+ ...pngDimensions(png)
46273
+ };
46274
+ }),
46275
+ recording: (session, action) => run("recording", async (signal) => {
46276
+ const client = await forSession(session, signal);
46277
+ const key = session.leaseId;
46278
+ if (action === "start" && recordings.has(key)) throw new Error("This device session is already recording.");
46279
+ if (action === "stop" && !recordings.has(key)) throw new Error("This device session has no active recording.");
46280
+ const outputFile = recordings.get(key)?.outputFile ?? `${cwd}/device-recording-${randomUUID()}.mp4`;
46281
+ const result = xcodeResultData(await client.call("record_sim_video", {
46282
+ simulatorId: session.deviceId,
46283
+ ...action === "start" ? { start: true } : {
46284
+ stop: true,
46285
+ outputFile
46286
+ }
46287
+ }, signal));
46288
+ if (action === "start") recordings.set(key, {
46289
+ outputFile,
46290
+ simulatorId: session.deviceId
46291
+ });
46292
+ else recordings.delete(key);
46293
+ return result;
46294
+ }),
46295
+ closeSession,
46296
+ stop: () => {
46297
+ for (const key of clients.keys()) closeSession(key);
46298
+ }
46299
+ };
46300
+ }
46301
+ //#endregion
46302
+ //#region src/device/AppleDeviceAdapter.ts
46303
+ const MAX_HIERARCHY_BYTES = 512 * 1024;
46304
+ const MAX_SESSIONS = 32;
46305
+ const REQUIRED_DEVICE_TOOLS = [
46306
+ "DeviceInteractionStartSession",
46307
+ "DeviceInteractionSynthesize",
46308
+ "DeviceInteractionEndSession"
46309
+ ];
46310
+ const FRAME_PATTERN = /\{\{(-?[\d.]+),\s*(-?[\d.]+)\},\s*\{([\d.]+),\s*([\d.]+)\}\}/;
46311
+ const HIT_PATTERN = /hitPoint:\s*\{(-?[\d.]+),\s*(-?[\d.]+)\}/;
46312
+ const Capture = Schema$1.Struct({
46313
+ hierarchyPath: Schema$1.String,
46314
+ screenshotPath: Schema$1.String
46315
+ });
46316
+ const Session = Schema$1.Struct({
46317
+ interactionSessionKey: Schema$1.String,
46318
+ deviceUUID: Schema$1.String,
46319
+ deviceIsSimulator: Schema$1.Boolean
46320
+ });
46321
+ const Simulators = Schema$1.Struct({ devices: Schema$1.Record(Schema$1.String, Schema$1.Array(Schema$1.Struct({
46322
+ udid: Schema$1.String,
46323
+ name: Schema$1.String,
46324
+ state: Schema$1.String,
46325
+ isAvailable: Schema$1.Boolean
46326
+ }))) });
46327
+ function appleMcpData(result) {
46328
+ if (result.structuredContent) return objectValue(result.structuredContent);
46329
+ for (const block of Array.isArray(result.content) ? result.content : []) {
46330
+ const text = objectValue(block).text;
46331
+ if (typeof text !== "string") continue;
46332
+ try {
46333
+ return objectValue(JSON.parse(text));
46334
+ } catch {}
46335
+ }
46336
+ throw new Error("Apple MCP returned no structured device result.");
46337
+ }
46338
+ /** Use Apple's documented hierarchy hit points, never guessed screenshot coordinates. */
46339
+ function appleHierarchySnapshot(text, sequence) {
46340
+ const elements = text.split("\n").flatMap((line, index) => {
46341
+ const frame = line.match(FRAME_PATTERN);
46342
+ const hit = line.match(HIT_PATTERN);
46343
+ if (!frame || !hit || line.includes("isRemoteLeafPlaceholder")) return [];
46344
+ const [x, y, width, height] = frame.slice(1).map(Number);
46345
+ const [hx, hy] = hit.slice(1).map(Number);
46346
+ if (![
46347
+ x,
46348
+ y,
46349
+ width,
46350
+ height,
46351
+ hx,
46352
+ hy
46353
+ ].every((value) => value !== void 0 && Number.isFinite(value)) || width <= 0 || height <= 0) return [];
46354
+ return [{
46355
+ ref: `a${sequence}-${index}`,
46356
+ label: line.trim(),
46357
+ frame: {
46358
+ x,
46359
+ y,
46360
+ width,
46361
+ height
46362
+ },
46363
+ hitPoint: {
46364
+ x: hx,
46365
+ y: hy
46366
+ },
46367
+ role: /\b(?:UI)?Window\b/.test(line) ? "window" : "element",
46368
+ actions: ["tap"]
46369
+ }];
46370
+ });
46371
+ return {
46372
+ screenHash: createHash("sha256").update(text).digest("hex"),
46373
+ seq: sequence,
46374
+ elements
46375
+ };
46376
+ }
46377
+ function createAppleDeviceAdapter(input) {
46378
+ const sessions = /* @__PURE__ */ new Map();
46379
+ const fail = (operation) => (cause) => new DeviceOperationError({
46380
+ operation,
46381
+ reason: "request_failed",
46382
+ cause: cause instanceof Error ? cause.message : String(cause)
46383
+ });
46384
+ const call = (client, name, args) => Effect.tryPromise({
46385
+ try: (signal) => client.call(name, args, signal),
46386
+ catch: fail(name)
46387
+ });
46388
+ const closeSession = (leaseId) => {
46389
+ const pending = sessions.get(leaseId);
46390
+ sessions.delete(leaseId);
46391
+ pending?.then(async (owned) => {
46392
+ try {
46393
+ await owned.client.call("DeviceInteractionEndSession", { interactionSessionKey: owned.key }, new AbortController().signal);
46394
+ } finally {
46395
+ owned.client.close();
46396
+ }
46397
+ }).catch(() => {});
46398
+ };
46399
+ const get = (session) => Effect.tryPromise({
46400
+ try: async (signal) => {
46401
+ if (session.hostId !== "local" || session.platform !== "ios" || !session.leaseId) throw new Error("Apple MCP requires an exact local iOS device lease.");
46402
+ let pending = sessions.get(session.leaseId);
46403
+ if (!pending) {
46404
+ if (sessions.size >= MAX_SESSIONS) throw new Error("Close an existing device session first.");
46405
+ pending = (async () => {
46406
+ const client = await XcodeMcpClient.start("/usr/bin/xcrun", input.cwd, {
46407
+ ...input.environment,
46408
+ MCP_XCODE_SESSION_ID: randomUUID()
46409
+ }, signal, "apple");
46410
+ try {
46411
+ const missing = REQUIRED_DEVICE_TOOLS.filter((tool) => !client.tools.has(tool));
46412
+ if (missing.length > 0) throw new Error(`Selected Xcode lacks required device tools: ${missing.join(", ")}. No device session was opened.`);
46413
+ if (input.workspacePath) await client.call("XcodeOpenWorkspace", { path: input.workspacePath }, signal);
46414
+ const opened = Schema$1.decodeUnknownSync(Session)(appleMcpData(await client.call("DeviceInteractionStartSession", {
46415
+ deviceIdentifier: session.deviceId,
46416
+ sessionIdentifier: `P4 Device ${session.threadId.slice(0, 8)}`
46417
+ }, signal)));
46418
+ if (opened.deviceUUID.toLowerCase() !== session.deviceId.toLowerCase() || !opened.deviceIsSimulator) {
46419
+ await client.call("DeviceInteractionEndSession", { interactionSessionKey: opened.interactionSessionKey }, signal);
46420
+ throw new Error("Apple selected a different device. No interaction was performed.");
46421
+ }
46422
+ return {
46423
+ client,
46424
+ key: opened.interactionSessionKey,
46425
+ sequence: 0
46426
+ };
46427
+ } catch (error) {
46428
+ client.close();
46429
+ throw error;
46430
+ }
46431
+ })();
46432
+ sessions.set(session.leaseId, pending);
46433
+ const leaseId = session.leaseId;
46434
+ pending.catch(() => {
46435
+ if (sessions.get(leaseId) === pending) sessions.delete(leaseId);
46436
+ });
46437
+ }
46438
+ return pending;
46439
+ },
46440
+ catch: fail("open")
46441
+ });
46442
+ const capture = (owned, interactionCommand) => Effect.gen(function* () {
46443
+ const response = yield* call(owned.client, "DeviceInteractionSynthesize", {
46444
+ interactSessionKey: owned.key,
46445
+ ...interactionCommand ? { interactionCommand } : {}
46446
+ });
46447
+ const data = yield* Effect.try({
46448
+ try: () => appleMcpData(response),
46449
+ catch: fail("state")
46450
+ });
46451
+ const result = yield* Schema$1.decodeUnknownEffect(Capture)(data);
46452
+ if ((yield* input.fs.stat(result.hierarchyPath)).size > BigInt(MAX_HIERARCHY_BYTES)) return yield* fail("state")("Apple hierarchy exceeds the supported size.");
46453
+ const snapshot = appleHierarchySnapshot(yield* input.fs.readFileString(result.hierarchyPath), owned.sequence + 1);
46454
+ if (owned.snapshot?.screenHash !== snapshot.screenHash) {
46455
+ owned.snapshot = snapshot;
46456
+ owned.sequence += 1;
46457
+ }
46458
+ const currentSnapshot = owned.snapshot;
46459
+ owned.image = result.screenshotPath;
46460
+ const viewport = currentSnapshot.elements.find((element) => element.role === "window")?.frame;
46461
+ return {
46462
+ adapter: "apple-xcode",
46463
+ capabilities: [
46464
+ "snapshot_ui",
46465
+ "tap",
46466
+ "screenshot"
46467
+ ],
46468
+ capture: currentSnapshot,
46469
+ ...viewport ? { viewport } : {}
46470
+ };
46471
+ }).pipe(Effect.mapError(fail("state")));
46472
+ return {
46473
+ diagnose: Effect.gen(function* () {
46474
+ const result = yield* input.run("xcrun", [
46475
+ "mcp-server",
46476
+ "status",
46477
+ "--format",
46478
+ "json"
46479
+ ]);
46480
+ if (result.code !== 0) return "Apple MCP did not respond. Check the selected Xcode installation and MCP service.";
46481
+ const status = objectValue(yield* Schema$1.decodeUnknownEffect(Schema$1.UnknownFromJsonString)(result.stdout));
46482
+ const permission = objectValue(status.permission);
46483
+ if ((Array.isArray(permission.pendingAgentApprovals) ? permission.pendingAgentApprovals : []).some((item) => objectValue(objectValue(objectValue(objectValue(item).subject).agent)._0).name === "p4code-device-adapter")) return "Apple Xcode is waiting for approval of p4code-device-adapter and its project folder. Approve the pending request in Xcode, then select the device again. P4 cannot grant this approval automatically.";
46484
+ if (status.running === false) return "Apple MCP service is not running. Open the selected Xcode project or configure its headless service, then select the device again.";
46485
+ return "Apple MCP did not respond in time. Check Xcode agent/folder permissions and session logs before selecting the device again.";
46486
+ }).pipe(Effect.orElseSucceed(() => "Apple MCP did not respond. Check Xcode agent/folder permissions and service status.")),
46487
+ discover: Effect.gen(function* () {
46488
+ const result = yield* input.run("xcrun", [
46489
+ "simctl",
46490
+ "list",
46491
+ "devices",
46492
+ "available",
46493
+ "--json"
46494
+ ]);
46495
+ if (result.code !== 0) return yield* fail("list")("Selected Xcode cannot list simulators.");
46496
+ const parsed = yield* Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(Simulators))(result.stdout).pipe(Effect.mapError(fail("list")));
46497
+ return Object.entries(parsed.devices).filter(([runtime]) => runtime.includes(".iOS-")).flatMap(([runtime, devices]) => devices.filter((device) => device.isAvailable).map((device) => ({
46498
+ hostId: "local",
46499
+ id: device.udid,
46500
+ platform: "ios",
46501
+ name: device.name,
46502
+ version: runtime.replace("com.apple.CoreSimulator.SimRuntime.", "").replaceAll("-", "."),
46503
+ physical: false,
46504
+ booted: device.state === "Booted"
46505
+ })));
46506
+ }),
46507
+ state: (session, sinceScreenHash) => Effect.gen(function* () {
46508
+ const result = yield* capture(yield* get(session));
46509
+ return sinceScreenHash === result.capture.screenHash ? {
46510
+ ...result,
46511
+ capture: {
46512
+ screenHash: result.capture.screenHash,
46513
+ seq: result.capture.seq,
46514
+ unchanged: true
46515
+ }
46516
+ } : result;
46517
+ }),
46518
+ action: (session, action) => Effect.gen(function* () {
46519
+ if (action.action !== "tap") return yield* fail("command")("This Apple MCP integration currently advertises tap only. Use only capabilities returned by getAXState().");
46520
+ const owned = yield* get(session);
46521
+ const previous = owned.snapshot;
46522
+ const element = previous?.elements.find((element) => element.ref === action.elementRef);
46523
+ if (!element) return yield* fail("command")("Stale device ref. Read getAXState() and choose a current element.");
46524
+ const fresh = yield* capture(owned);
46525
+ if (fresh.capture.screenHash !== previous.screenHash) return yield* fail("command")("Device layout changed. Read getAXState() and choose a current element; no action was performed.");
46526
+ return {
46527
+ ...yield* capture(owned, `t ${element.hitPoint.x} ${element.hitPoint.y}`),
46528
+ ...fresh.viewport ? { pointer: {
46529
+ x: (element.hitPoint.x - fresh.viewport.x) / fresh.viewport.width,
46530
+ y: (element.hitPoint.y - fresh.viewport.y) / fresh.viewport.height
46531
+ } } : {}
46532
+ };
46533
+ }),
46534
+ screenshot: (session) => Effect.gen(function* () {
46535
+ const owned = yield* get(session);
46536
+ yield* capture(owned);
46537
+ if ((yield* input.fs.stat(owned.image)).size > BigInt(8388608)) return yield* fail("screenshot")("Apple screenshot exceeds the supported size.");
46538
+ const png = Buffer.from(yield* input.fs.readFile(owned.image));
46539
+ return {
46540
+ png,
46541
+ ...yield* Effect.try({
46542
+ try: () => pngDimensions(png),
46543
+ catch: fail("screenshot")
46544
+ })
46545
+ };
46546
+ }).pipe(Effect.mapError(fail("screenshot"))),
46547
+ recording: (_session, _action) => Effect.fail(fail("recording")("Apple MCP does not advertise a recording tool. Recording is unavailable through this adapter.")),
46548
+ closeSession,
46549
+ stop: () => {
46550
+ for (const key of sessions.keys()) closeSession(key);
46551
+ }
46552
+ };
46553
+ }
46554
+ //#endregion
45606
46555
  //#region src/device/DeviceToolchain.ts
45607
46556
  /**
45608
46557
  * Pinned installs of the two external tools device support is built on.
@@ -45818,6 +46767,7 @@ function deviceHubPlatform(platforms) {
45818
46767
  const COMMAND_TIMEOUT_MS$1 = 2e4;
45819
46768
  const COMMAND_OUTPUT_LIMIT = 1024 * 1024;
45820
46769
  const COMMAND_FAILURE_CODE = 127;
46770
+ const APPLE_DEVICE_MCP_MINIMUM_XCODE = 27;
45821
46771
  const make$81 = Effect.fn("LocalDeviceHost.make")(function* () {
45822
46772
  const config = yield* ServerConfig$1;
45823
46773
  const fs = yield* FileSystem.FileSystem;
@@ -45864,7 +46814,19 @@ const make$81 = Effect.fn("LocalDeviceHost.make")(function* () {
45864
46814
  stderr: "Device command failed.",
45865
46815
  code: COMMAND_FAILURE_CODE
45866
46816
  })));
46817
+ const appleVersion = (platform === "darwin" ? yield* run("xcrun", ["--find", "mcpbridge"]) : null)?.code === 0 ? yield* run("xcodebuild", ["-version"]) : null;
46818
+ const appleAvailable = appleVersion?.code === 0 && Number(appleVersion.stdout.match(/^Xcode (\d+)\b/m)?.[1]) >= APPLE_DEVICE_MCP_MINIMUM_XCODE;
46819
+ const legacyPath = environment.P4CODE_XCODEBUILDMCP_PATH?.trim();
46820
+ const useLegacy = environment.P4CODE_DEVICE_ADAPTER === "xcodebuildmcp";
46821
+ const xcode = appleAvailable && !useLegacy ? createAppleDeviceAdapter({
46822
+ cwd: config.stateDir,
46823
+ environment: hostEnvironment,
46824
+ fs,
46825
+ run,
46826
+ ...environment.P4CODE_APPLE_MCP_WORKSPACE ? { workspacePath: environment.P4CODE_APPLE_MCP_WORKSPACE } : {}
46827
+ }) : platform === "darwin" && legacyPath ? createXcodeDeviceAdapter(legacyPath, config.stateDir, hostEnvironment) : void 0;
45867
46828
  const stopAgent = Effect.suspend(() => {
46829
+ xcode?.stop();
45868
46830
  const owned = agent;
45869
46831
  agent = null;
45870
46832
  return owned?.stop ?? Effect.void;
@@ -45903,7 +46865,8 @@ const make$81 = Effect.fn("LocalDeviceHost.make")(function* () {
45903
46865
  nodePath: process.execPath,
45904
46866
  hub: { origin: started.origin },
45905
46867
  helpers,
45906
- run
46868
+ run,
46869
+ ...xcode ? { xcode } : {}
45907
46870
  };
45908
46871
  return ready;
45909
46872
  }, Effect.onExit((exit) => Exit.isFailure(exit) ? stop : Effect.void));
@@ -45914,6 +46877,10 @@ const make$81 = Effect.fn("LocalDeviceHost.make")(function* () {
45914
46877
  id: hostId,
45915
46878
  kind: "local",
45916
46879
  label: "This machine",
46880
+ ...xcode ? { automation: {
46881
+ provider: appleAvailable && !useLegacy ? "apple-xcode" : "xcodebuildmcp",
46882
+ detail: appleAvailable && !useLegacy ? "Apple Xcode MCP. Agent and folder approval is required; P4 never grants it automatically." : "Optional XcodeBuildMCP 2.7.0 executable. No automatic installation."
46883
+ } } : {},
45917
46884
  platforms: yield* Effect.all([platformAvailability("ios"), platformAvailability("android")]),
45918
46885
  hubInstalled: yield* isDeviceHubInstalled(config.baseDir).pipe(Effect.provideContext(context)),
45919
46886
  agentDeviceInstalled: yield* isAgentDeviceInstalled(config.baseDir).pipe(Effect.provideContext(context))
@@ -45925,8 +46892,9 @@ const make$81 = Effect.fn("LocalDeviceHost.make")(function* () {
45925
46892
  return ready;
45926
46893
  })),
45927
46894
  ensureReady: (onPhase) => lock.withPermit(ensureHub(onPhase)),
45928
- ensureAgentReady: (onPhase) => lock.withPermit(Effect.gen(function* () {
46895
+ ensureAgentReady: (onPhase, devicePlatform) => lock.withPermit(Effect.gen(function* () {
45929
46896
  const host = yield* ensureHub(onPhase);
46897
+ if (xcode && devicePlatform === "ios") return host;
45930
46898
  if (agent && (yield* agent.isRunning)) return {
45931
46899
  ...host,
45932
46900
  agentDevice: agent.endpoint
@@ -45953,7 +46921,7 @@ const make$81 = Effect.fn("LocalDeviceHost.make")(function* () {
45953
46921
  stop: lock.withPermit(stop)
45954
46922
  };
45955
46923
  });
45956
- Layer.effect(DeviceHost, make$81()).pipe(Layer.provide(layer$72));
46924
+ Layer.effect(DeviceHost, make$81()).pipe(Layer.provide(layer$73));
45957
46925
  Data.TaggedError("SshHostDiscoveryError");
45958
46926
  var SshInvalidTargetError = class extends Data.TaggedError("SshInvalidTargetError") {};
45959
46927
  var SshCommandError = class extends Data.TaggedError("SshCommandError") {};
@@ -46952,9 +47920,17 @@ var DeviceService = class extends Context.Service()("@p4code/cli/device/DeviceSe
46952
47920
  const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input) {
46953
47921
  const settings = yield* ServerSettingsService;
46954
47922
  const scope = yield* Scope.Scope;
46955
- const registry = yield* make$82(input);
46956
- const commands = yield* make$84();
46957
- const threads = yield* make$83();
47923
+ const registry = yield* make$83(input);
47924
+ const commands = yield* make$85();
47925
+ const adapterLeases = /* @__PURE__ */ new Map();
47926
+ const closeCommands = (sessions) => Effect.sync(() => {
47927
+ for (const session of sessions) {
47928
+ if (!session.leaseId) continue;
47929
+ adapterLeases.get(session.leaseId)?.();
47930
+ adapterLeases.delete(session.leaseId);
47931
+ }
47932
+ }).pipe(Effect.andThen(commands.close(sessions)));
47933
+ const threads = yield* make$84();
46958
47934
  const changes = yield* Effect.acquireRelease(PubSub.sliding(STATE_BUFFER_SIZE), PubSub.shutdown);
46959
47935
  const stateLock = yield* Semaphore.make(1);
46960
47936
  const discoveryLock = yield* Semaphore.make(1);
@@ -46991,7 +47967,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
46991
47967
  }));
46992
47968
  }
46993
47969
  const retireSessions = (ids) => Effect.gen(function* () {
46994
- yield* commands.close(state.sessions.filter((session) => ids.includes(session.hostId)));
47970
+ yield* closeCommands(state.sessions.filter((session) => ids.includes(session.hostId)));
46995
47971
  yield* update((current) => {
46996
47972
  for (const id of ids) hostLocks.delete(id);
46997
47973
  return {
@@ -47202,7 +48178,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
47202
48178
  return yield* withHost(value.hostId, (lease) => Effect.gen(function* () {
47203
48179
  let found = yield* freshDevice(lease, value);
47204
48180
  if (access === "agent") {
47205
- yield* lease.host.ensureAgentReady(() => Effect.void);
48181
+ yield* lease.host.ensureAgentReady(() => Effect.void, value.platform);
47206
48182
  yield* registry.commit(lease, () => void 0);
47207
48183
  }
47208
48184
  if (!found.device.booted && value.boot !== false) {
@@ -47241,7 +48217,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
47241
48217
  openedAt,
47242
48218
  leaseId: NodeCrypto.randomUUID()
47243
48219
  };
47244
- yield* commands.close(sessionsToClose(state.sessions, session));
48220
+ yield* closeCommands(sessionsToClose(state.sessions, session));
47245
48221
  yield* update((current) => ({
47246
48222
  ...current,
47247
48223
  sessions: [...current.sessions.filter((item) => !(item.threadId === session.threadId && item.hostId === session.hostId && item.deviceId === session.deviceId)), session]
@@ -47252,7 +48228,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
47252
48228
  const shutdownWithin = Effect.fn("DeviceService.shutdownWithin")(function* (lease, value) {
47253
48229
  const { ready, device } = yield* freshDevice(lease, value);
47254
48230
  yield* input.actions.shutdown(ready, device);
47255
- yield* commands.close(state.sessions.filter((session) => session.hostId === value.hostId && session.deviceId === value.deviceId));
48231
+ yield* closeCommands(state.sessions.filter((session) => session.hostId === value.hostId && session.deviceId === value.deviceId));
47256
48232
  yield* update((current) => ({
47257
48233
  ...current,
47258
48234
  devices: current.devices.map((item) => item.hostId === value.hostId && item.id === value.deviceId ? {
@@ -47276,7 +48252,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
47276
48252
  platform: session.platform
47277
48253
  });
47278
48254
  else {
47279
- yield* commands.close(sessionsToClose(state.sessions, value));
48255
+ yield* closeCommands(sessionsToClose(state.sessions, value));
47280
48256
  yield* update((current) => {
47281
48257
  const closing = sessionsToClose(current.sessions, value);
47282
48258
  return {
@@ -47319,11 +48295,16 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
47319
48295
  });
47320
48296
  const validate = registry.isCurrent(lease).pipe(Effect.map((current) => current && state.sessions.includes(session)));
47321
48297
  return yield* commands.run(session, validate, Effect.gen(function* () {
47322
- const ready = yield* lease.host.ensureAgentReady(() => Effect.void);
48298
+ const ready = yield* lease.host.ensureAgentReady(() => Effect.void, session.platform);
47323
48299
  if (!(yield* validate)) return yield* new DeviceHostUnavailableError({
47324
48300
  hostId: target.hostId,
47325
48301
  reason: "Device session or agent authorization changed."
47326
48302
  });
48303
+ if (ready.xcode && session.leaseId) {
48304
+ const adapter = ready.xcode;
48305
+ const leaseId = session.leaseId;
48306
+ adapterLeases.set(leaseId, () => adapter.closeSession(leaseId));
48307
+ }
47327
48308
  return yield* execute(ready, session);
47328
48309
  }));
47329
48310
  })).pipe(Effect.mapError((error) => isDeviceHostError(error) || isDeviceHostTimeoutError(error) ? new DeviceHostUnavailableError({
@@ -47337,7 +48318,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
47337
48318
  agentList: listForAccess("agent"),
47338
48319
  configure,
47339
48320
  shutdown,
47340
- retireThread: (threadId) => threads.retire(threadId).pipe(Effect.ensuring(Effect.suspend(() => commands.close(state.sessions.filter((session) => session.threadId === threadId))).pipe(Effect.ensuring(update((current) => ({
48321
+ retireThread: (threadId) => threads.retire(threadId).pipe(Effect.ensuring(Effect.suspend(() => closeCommands(state.sessions.filter((session) => session.threadId === threadId))).pipe(Effect.ensuring(update((current) => ({
47341
48322
  ...current,
47342
48323
  sessions: current.sessions.filter((session) => session.threadId !== threadId),
47343
48324
  bootingDevices: (current.bootingDevices ?? []).filter((device) => device.threadId !== threadId)
@@ -47345,7 +48326,10 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
47345
48326
  restoreThread: threads.restore,
47346
48327
  withAgentDevice: (target, execute) => threads.run(target.threadId, withAgentDevice(target, execute)),
47347
48328
  withManualDevice: (target, execute) => threads.run(target.threadId, withManualDevice(target, execute)),
47348
- withAgentCommand: (target, execute) => threads.run(target.threadId, withAgentDevice(target, (ready, session) => execute(ready.agentDevice, session))),
48329
+ withAgentCommand: (target, execute) => threads.run(target.threadId, withAgentDevice(target, (ready, session) => ready.agentDevice ? execute(ready.agentDevice, session) : Effect.fail(new DeviceHostUnavailableError({
48330
+ hostId: target.hostId,
48331
+ reason: "This host uses semantic device control. Use cua.getDevice() and element refs."
48332
+ })))),
47349
48333
  open: (value) => threads.run(value.threadId, openWithAccess(value, "manual")),
47350
48334
  agentOpen: (value) => threads.run(value.threadId, openWithAccess(value, "agent")),
47351
48335
  close: (value) => closeWithAccess(value, "manual"),
@@ -47358,15 +48342,15 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
47358
48342
  };
47359
48343
  });
47360
48344
  const make$79 = Effect.gen(function* () {
47361
- const discover = yield* make$86();
47362
- const actions = yield* make$85();
48345
+ const discover = yield* make$87();
48346
+ const actions = yield* make$86();
47363
48347
  return yield* makeWithHosts({
47364
48348
  local: make$81(),
47365
48349
  ssh: make$80,
47366
48350
  discover,
47367
48351
  actions
47368
48352
  });
47369
- }).pipe(Effect.provide(layer$72));
48353
+ }).pipe(Effect.provide(layer$73));
47370
48354
  const layer$70 = Layer.effect(DeviceService, make$79);
47371
48355
  //#endregion
47372
48356
  //#region src/serverRuntimeStartup.ts
@@ -47979,7 +48963,7 @@ const projectCommandUuid = Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.
47979
48963
  operation: "generateProjectCommandId",
47980
48964
  cause
47981
48965
  })));
47982
- const ProjectCliRuntimeLive = Layer.mergeAll(layer$68, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$80), Layer.provideMerge(layerConfig)));
48966
+ const ProjectCliRuntimeLive = Layer.mergeAll(layer$68, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$81), Layer.provideMerge(layerConfig)));
47983
48967
  const PROJECT_CLI_LIVE_SERVER_TIMEOUT = Duration.seconds(1);
47984
48968
  const withProjectCliSessionToken = (environmentAuth, run) => Effect.acquireUseRelease(environmentAuth.issueSession({
47985
48969
  scopes: AuthAdministrativeScopes,
@@ -48070,7 +49054,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
48070
49054
  });
48071
49055
  yield* Console.log(output);
48072
49056
  }));
48073
- const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$117(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
49057
+ const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$118(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
48074
49058
  return yield* Effect.gen(function* () {
48075
49059
  const snapshot = yield* getOfflineSnapshot();
48076
49060
  const orchestrationEngine = yield* OrchestrationEngineService;
@@ -48081,7 +49065,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
48081
49065
  });
48082
49066
  yield* Console.log(output);
48083
49067
  }).pipe(Effect.provide(offlineRuntimeLayer));
48084
- }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$68).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$117(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
49068
+ }).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$68).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$118(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
48085
49069
  });
48086
49070
  const projectAddCommand = Command.make("add", {
48087
49071
  ...projectLocationFlags,
@@ -53298,80 +54282,6 @@ const make$75 = Effect.fn("DeviceCommandProxy.make")(function* () {
53298
54282
  var DeviceCommandProxy = class extends Context.Service()("@p4code/cli/device/DeviceCommandProxy") {};
53299
54283
  const layer$67 = Layer.effect(DeviceCommandProxy, make$75().pipe(Effect.map((execute) => ({ execute }))));
53300
54284
  //#endregion
53301
- //#region src/device/DeviceScreenshot.ts
53302
- const MAX_SCREENSHOT_BYTES = 8 * 1024 * 1024;
53303
- var DeviceScreenshotFailure = class extends Data.TaggedError("DeviceScreenshotFailure") {};
53304
- const MAX_DIMENSION = 16384;
53305
- const MAX_PIXELS = 64 * 1024 * 1024;
53306
- const SCREENSHOT_TIMEOUT_MS = 2e4;
53307
- const PNG_HEADER_BYTES = 33;
53308
- const PNG_CHUNK_OVERHEAD = 12;
53309
- const PNG_SIGNATURE = Buffer.from([
53310
- 137,
53311
- 80,
53312
- 78,
53313
- 71,
53314
- 13,
53315
- 10,
53316
- 26,
53317
- 10
53318
- ]);
53319
- function pngDimensions(png) {
53320
- const buffer = Buffer.from(png.buffer, png.byteOffset, png.byteLength);
53321
- if (buffer.length < PNG_HEADER_BYTES || buffer.length > 8388608 || !buffer.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE) || buffer.readUInt32BE(8) !== 13 || buffer.toString("ascii", 12, 16) !== "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG screenshot." });
53322
- const width = buffer.readUInt32BE(16);
53323
- const height = buffer.readUInt32BE(20);
53324
- if (!width || !height || width > MAX_DIMENSION || height > MAX_DIMENSION || width * height > MAX_PIXELS) throw new DeviceScreenshotFailure({ message: "Screenshot dimensions exceed limits." });
53325
- let offset = PNG_HEADER_BYTES;
53326
- let imageData = false;
53327
- let ended = false;
53328
- while (offset + PNG_CHUNK_OVERHEAD <= buffer.length) {
53329
- const length = buffer.readUInt32BE(offset);
53330
- const type = buffer.toString("ascii", offset + 4, offset + 8);
53331
- const next = offset + PNG_CHUNK_OVERHEAD + length;
53332
- if (next > buffer.length || type === "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG chunk." });
53333
- if (type === "IDAT") imageData = true;
53334
- if (type === "IEND") {
53335
- ended = length === 0 && next === buffer.length;
53336
- break;
53337
- }
53338
- offset = next;
53339
- }
53340
- if (!imageData || !ended) throw new DeviceScreenshotFailure({ message: "Incomplete PNG screenshot." });
53341
- return {
53342
- width,
53343
- height
53344
- };
53345
- }
53346
- const make$74 = Effect.fn("DeviceScreenshot.make")(function* () {
53347
- const http = yield* HttpClient.HttpClient;
53348
- return Effect.fn("DeviceScreenshot.capture")(function* (ready, session) {
53349
- const origin = yield* Effect.try(() => new URL(ready.hub.origin));
53350
- if (origin.protocol !== "http:" || origin.hostname !== "127.0.0.1" || !origin.port || origin.username || origin.password || origin.pathname !== "/" || origin.search || origin.hash) return yield* new DeviceScreenshotFailure({ message: "Invalid owned hub." });
53351
- const vendor = session.platform === "ios" ? "serve-sim" : "serve-emu";
53352
- const response = yield* http.execute(HttpClientRequest.post(`${origin.origin}/vendor/${vendor}/api/screenshot?device=${encodeURIComponent(session.deviceId)}`));
53353
- if (response.status !== 200) return yield* new DeviceScreenshotFailure({ message: "Screenshot rejected." });
53354
- let bytes = 0;
53355
- const chunks = [];
53356
- yield* response.stream.pipe(Stream.runForEach((chunk) => Effect.gen(function* () {
53357
- bytes += chunk.byteLength;
53358
- if (bytes > 8388608) return yield* new DeviceScreenshotFailure({ message: "Screenshot too large." });
53359
- chunks.push(chunk);
53360
- })));
53361
- const png = Buffer.concat(chunks, bytes);
53362
- return {
53363
- png,
53364
- ...yield* Effect.try(() => pngDimensions(png))
53365
- };
53366
- }, Effect.scoped, Effect.timeout(Duration.millis(SCREENSHOT_TIMEOUT_MS)), Effect.mapError(() => new DeviceOperationError({
53367
- operation: "screenshot",
53368
- reason: "request_failed",
53369
- cause: "Device screenshot could not be captured."
53370
- })));
53371
- });
53372
- var DeviceScreenshot = class extends Context.Service()("@p4code/cli/device/DeviceScreenshot") {};
53373
- const layer$66 = Layer.effect(DeviceScreenshot, make$74().pipe(Effect.map((capture) => ({ capture }))));
53374
- //#endregion
53375
54285
  //#region src/device/DeviceHubRoute.ts
53376
54286
  const DEVICE_HUB_PROXY_PREFIX = "/api/device-hub";
53377
54287
  const MAX_ROUTE_LENGTH = 16 * 1024;
@@ -53574,12 +54484,12 @@ const requireThreadSpawn = Effect.fn("mcp.requireThreadSpawn")(function* (fusion
53574
54484
  const isDeviceToolUnavailableError = Schema$1.is(DeviceToolUnavailableError);
53575
54485
  const isDeviceActionUnavailableError = Schema$1.is(DeviceActionUnavailableError);
53576
54486
  /** Internal bridge; tool payloads never choose thread identity or daemon credentials. */
53577
- const withDeviceInvocation = (execute) => Effect.gen(function* () {
54487
+ const withDeviceInvocation = (execute, capability = "devices") => Effect.gen(function* () {
53578
54488
  const invocation = yield* McpInvocationContext;
53579
54489
  const service = yield* DeviceService;
53580
54490
  const lifetime = invocation.authorizationLifetime;
53581
54491
  const denied = () => new DeviceToolUnavailableError({ reason: "Device command unavailable or authorization changed." });
53582
- if (!invocation.capabilities.has("devices") || !lifetime?.revocation || lifetime.revoked) return yield* denied();
54492
+ if (!invocation.capabilities.has(capability) || !lifetime?.revocation || lifetime.revoked) return yield* denied();
53583
54493
  const work = execute(service, invocation.threadId).pipe(Effect.tap(() => lifetime.revoked ? Effect.fail(denied()) : Effect.void), Effect.mapError((error) => isDeviceToolUnavailableError(error) || isDeviceActionUnavailableError(error) ? error : denied()));
53584
54494
  return yield* Effect.raceFirst(work, Deferred.await(lifetime.revocation).pipe(Effect.andThen(Effect.fail(denied()))));
53585
54495
  });
@@ -53594,6 +54504,7 @@ const MAX_AX_BYTES = 512 * 1024;
53594
54504
  const AX_TIMEOUT_MS = 1e4;
53595
54505
  const MAX_AX_EXTENT = 32768;
53596
54506
  const MIN_AX_EXTENT = 2;
54507
+ const isToolUnavailable = Schema$1.is(DeviceToolUnavailableError);
53597
54508
  const AxFrames = Schema$1.Array(Schema$1.Struct({ frame: Schema$1.Struct({
53598
54509
  x: Schema$1.Number,
53599
54510
  y: Schema$1.Number,
@@ -53633,6 +54544,7 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
53633
54544
  const state = yield* service.agentList;
53634
54545
  const device = state.devices.find((device) => device.hostId === input.hostId && device.id === input.deviceId && device.platform === input.platform);
53635
54546
  if (!device) return yield* stale();
54547
+ if (device.physical) return yield* new DeviceToolUnavailableError({ reason: "Physical-device UI control is not supported by this adapter. Pairing, trust, Developer Mode and signing must be configured separately; use supported device-management workflows." });
53636
54548
  const session = yield* service.agentOpen({
53637
54549
  threadId,
53638
54550
  hostId: input.hostId,
@@ -53670,8 +54582,16 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
53670
54582
  deviceId: target.deviceId
53671
54583
  }, (ready, session) => Effect.gen(function* () {
53672
54584
  if (session.leaseId !== input.leaseId) return yield* stale();
54585
+ if (input.method === "recording") {
54586
+ if (!ready.xcode || session.platform !== "ios") return yield* new DeviceToolUnavailableError({ reason: "Recording is unavailable on this device adapter." });
54587
+ return yield* ready.xcode.recording(session, input.action).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) })));
54588
+ }
54589
+ if (input.method === "semantic") {
54590
+ if (!ready.xcode || session.platform !== "ios") return yield* new DeviceToolUnavailableError({ reason: "Semantic device actions require a supported local Xcode adapter. Use the capabilities returned by getAXState()." });
54591
+ return yield* ready.xcode.action(session, input.action).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) })));
54592
+ }
53673
54593
  if (input.method === "screenshot") {
53674
- const capture = yield* screenshots.capture(ready, session);
54594
+ const capture = yield* ready.xcode && session.platform === "ios" ? ready.xcode.screenshot(session).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) }))) : screenshots.capture(ready, session);
53675
54595
  return {
53676
54596
  mimeType: "image/png",
53677
54597
  data: capture.png.toString("base64"),
@@ -53679,6 +54599,7 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
53679
54599
  height: capture.height
53680
54600
  };
53681
54601
  }
54602
+ if (ready.xcode && session.platform === "ios") return yield* ready.xcode.state(session, input.sinceScreenHash).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) })));
53682
54603
  if (session.platform !== "ios") return yield* new DeviceToolUnavailableError({ reason: "CUA accessibility state is currently supported only for iOS Simulator. Use a screenshot for this device." });
53683
54604
  const route = resolveDeviceHubRoute({
53684
54605
  target: {
@@ -53706,12 +54627,13 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
53706
54627
  leaseId: session.leaseId,
53707
54628
  accessibility
53708
54629
  };
53709
- }).pipe(Effect.scoped, Effect.timeout(AX_TIMEOUT_MS), Effect.mapError((error) => new DeviceOperationError({
53710
- operation: "command",
53711
- reason: "request_failed",
53712
- cause: error.message
53713
- }))));
53714
- })).pipe(Effect.provideService(McpInvocationContext, scope), Effect.provideService(DeviceService, devices), Effect.mapError((error) => new ComputerUseError({
54630
+ }).pipe(Effect.scoped, Effect.timeoutOrElse({
54631
+ duration: AX_TIMEOUT_MS,
54632
+ orElse: () => Effect.gen(function* () {
54633
+ return yield* new DeviceToolUnavailableError({ reason: ready.xcode && "diagnose" in ready.xcode ? yield* ready.xcode.diagnose : "Device state request timed out. Check device support before selecting the device again." });
54634
+ })
54635
+ }), Effect.mapError((error) => isToolUnavailable(error) ? error : new DeviceToolUnavailableError({ reason: error instanceof Error ? error.message : String(error) }))));
54636
+ }), "computer-use").pipe(Effect.provideService(McpInvocationContext, scope), Effect.provideService(DeviceService, devices), Effect.mapError((error) => new ComputerUseError({
53715
54637
  reason: "unavailable",
53716
54638
  detail: error.message
53717
54639
  })));
@@ -53737,7 +54659,7 @@ const hostSession = (scope) => ({
53737
54659
  providerSessionId: scope.providerSessionId
53738
54660
  });
53739
54661
  var ComputerUseBroker = class extends Context.Service()("@p4code/cli/mcp/ComputerUseBroker") {};
53740
- const layer$65 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
54662
+ const layer$66 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53741
54663
  const serviceScope = yield* Scope.Scope;
53742
54664
  const crypto = yield* Crypto.Crypto;
53743
54665
  const environment = yield* ServerEnvironment;
@@ -54328,7 +55250,7 @@ const renew = HttpRouter.add("POST", "/internal/computer-use/renew", Effect.gen(
54328
55250
  if (binding.trust) yield* (yield* RemoteComputerUseBindings).touch(bearer(request));
54329
55251
  return HttpServerResponse.empty({ status: 204 });
54330
55252
  }).pipe(Effect.catch(failure$1)));
54331
- const layer$64 = Layer.mergeAll(exchange, hubForward, hubExchange, connect, respond, device, report, environment, renew).pipe(Layer.provide(remoteComputerUseBindingsLayer));
55253
+ const layer$65 = Layer.mergeAll(exchange, hubForward, hubExchange, connect, respond, device, report, environment, renew).pipe(Layer.provide(remoteComputerUseBindingsLayer));
54332
55254
  //#endregion
54333
55255
  //#region src/device/DeviceHubSocketProtocol.ts
54334
55256
  const MAX_DEVICE_INPUT_BYTES = 8 * 1024;
@@ -55706,7 +56628,7 @@ const logP4ProjectFileLoadError = (error) => Effect.logWarning(error).pipe(Effec
55706
56628
  filePath: error.filePath,
55707
56629
  errorTag: error._tag
55708
56630
  }));
55709
- const make$73 = Effect.gen(function* () {
56631
+ const make$74 = Effect.gen(function* () {
55710
56632
  const fileSystem = yield* FileSystem.FileSystem;
55711
56633
  const path = yield* Path$1.Path;
55712
56634
  const load = Effect.fn("P4ProjectFileLoader.load")(function* (workspaceRoot) {
@@ -55727,7 +56649,7 @@ const make$73 = Effect.gen(function* () {
55727
56649
  });
55728
56650
  return P4ProjectFileLoader.of({ load });
55729
56651
  });
55730
- const layer$63 = Layer.effect(P4ProjectFileLoader, make$73);
56652
+ const layer$64 = Layer.effect(P4ProjectFileLoader, make$74);
55731
56653
  //#endregion
55732
56654
  //#region src/project/ProjectFaviconResolver.ts
55733
56655
  /**
@@ -55802,7 +56724,7 @@ function extractIconHref(source) {
55802
56724
  return null;
55803
56725
  }
55804
56726
  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) }));
55805
- const make$72 = Effect.gen(function* () {
56727
+ const make$73 = Effect.gen(function* () {
55806
56728
  const fileSystem = yield* FileSystem.FileSystem;
55807
56729
  const path = yield* Path$1.Path;
55808
56730
  const workspacePaths = yield* WorkspacePaths;
@@ -55871,7 +56793,7 @@ const make$72 = Effect.gen(function* () {
55871
56793
  });
55872
56794
  return ProjectFaviconResolver.of({ resolvePath });
55873
56795
  });
55874
- const layer$62 = Layer.effect(ProjectFaviconResolver, make$72);
56796
+ const layer$63 = Layer.effect(ProjectFaviconResolver, make$73);
55875
56797
  //#endregion
55876
56798
  //#region src/assets/AssetAccess.ts
55877
56799
  const ASSET_ROUTE_PREFIX = "/api/assets";
@@ -56282,10 +57204,10 @@ const resolveAsset = Effect.fn("AssetAccess.resolveAsset")(function* (token, rel
56282
57204
  //#endregion
56283
57205
  //#region src/observability/BrowserTraceCollector.ts
56284
57206
  var BrowserTraceCollector = class extends Context.Service()("@p4code/cli/observability/BrowserTraceCollector") {};
56285
- const make$71 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
57207
+ const make$72 = (sink) => BrowserTraceCollector.of({ record: (records) => Effect.sync(() => {
56286
57208
  for (const record of records) sink.push(record);
56287
57209
  }) });
56288
- const layer$61 = (sink) => Layer.succeed(BrowserTraceCollector, make$71(sink));
57210
+ const layer$62 = (sink) => Layer.succeed(BrowserTraceCollector, make$72(sink));
56289
57211
  //#endregion
56290
57212
  //#region src/auth/http.ts
56291
57213
  const CREDENTIAL_RESPONSE_HEADERS = {
@@ -57427,7 +58349,7 @@ const defaultCancelTimeout = Duration.seconds(15);
57427
58349
  const defaultSessionLoadTimeout = Duration.seconds(90);
57428
58350
  const defaultSessionLoadReplayIdleGap = Duration.seconds(2);
57429
58351
  var AcpSessionRuntime = class extends Context.Service()("@p4code/cli/provider/acp/AcpSessionRuntime") {};
57430
- const make$70 = (options) => Effect.gen(function* () {
58352
+ const make$71 = (options) => Effect.gen(function* () {
57431
58353
  const crypto = yield* Crypto.Crypto;
57432
58354
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
57433
58355
  const runtimeScope = yield* Scope.Scope;
@@ -57831,7 +58753,7 @@ const make$70 = (options) => Effect.gen(function* () {
57831
58753
  notify: acp.raw.notify
57832
58754
  };
57833
58755
  });
57834
- const layer$60 = (options) => Layer.effect(AcpSessionRuntime, make$70(options));
58756
+ const layer$61 = (options) => Layer.effect(AcpSessionRuntime, make$71(options));
57835
58757
  function sessionConfigOptionsFromSetup(response) {
57836
58758
  return response?.configOptions ?? [];
57837
58759
  }
@@ -58225,7 +59147,7 @@ const buildAntigravityPrompt = Effect.fn("buildAntigravityPrompt")(function* (in
58225
59147
  });
58226
59148
  /** Normal launches reject browser login; only the auth flow supplies `onAuthorizationUrl`. */
58227
59149
  const makeAntigravityAcpRuntime = Effect.fn("makeAntigravityAcpRuntime")(function* (input) {
58228
- const context = yield* Layer.build(layer$60({
59150
+ const context = yield* Layer.build(layer$61({
58229
59151
  ...input,
58230
59152
  authMethodId: input.authMethod ?? "oauth-personal",
58231
59153
  resumeMethod: "resume",
@@ -62202,7 +63124,7 @@ const BundledOnlyModelManifest = {
62202
63124
  refreshInBackground: Effect.void
62203
63125
  };
62204
63126
  Layer.succeed(ModelManifest, BundledOnlyModelManifest);
62205
- const make$69 = Effect.gen(function* () {
63127
+ const make$70 = Effect.gen(function* () {
62206
63128
  const fileSystem = yield* FileSystem.FileSystem;
62207
63129
  const path = yield* Path$1.Path;
62208
63130
  const config = yield* ServerConfig$1;
@@ -62247,7 +63169,7 @@ const make$69 = Effect.gen(function* () {
62247
63169
  refreshInBackground: Effect.forkIn(guardedRefresh, serviceScope).pipe(Effect.asVoid)
62248
63170
  });
62249
63171
  });
62250
- const layer$59 = Layer.effect(ModelManifest, make$69);
63172
+ const layer$60 = Layer.effect(ModelManifest, make$70);
62251
63173
  //#endregion
62252
63174
  //#region src/provider/ProviderDriver.ts
62253
63175
  function defaultProviderContinuationIdentity(input) {
@@ -62507,11 +63429,11 @@ const makeTextGenerationFromRegistry = (registry) => TextGeneration.of({
62507
63429
  detail: "This provider does not report account usage."
62508
63430
  }))))
62509
63431
  });
62510
- const make$68 = Effect.gen(function* () {
63432
+ const make$69 = Effect.gen(function* () {
62511
63433
  const registry = yield* ProviderInstanceRegistry;
62512
63434
  return makeTextGenerationFromRegistry(registry);
62513
63435
  });
62514
- const layer$58 = Layer.effect(TextGeneration, make$68);
63436
+ const layer$59 = Layer.effect(TextGeneration, make$69);
62515
63437
  //#endregion
62516
63438
  //#region src/provider/Layers/claudeUsageLimits.ts
62517
63439
  const SESSION_MINS$1 = 300;
@@ -64186,7 +65108,7 @@ function formatAskUserQuestionAnswers(answers) {
64186
65108
  * written as if the setting applied only to user-facing answers.
64187
65109
  */
64188
65110
  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.`;
64189
- const FUSION_BUILDER_INSTRUCTIONS = `You are Fusion Builder in an already-created native server pair. Server owns pairing and coordination. Do not inspect or invoke the Fusion skill, create/pair/rename threads, or announce/setup Fusion. Start the user's task directly. Before editing, create and maintain the phase list with your provider's step-tracking tool (Claude Code: TaskCreate for each phase, then TaskUpdate for status, or TodoWrite when that is the tool offered; Codex: update_plan), never the MCP task board tools - one entry per phase in order, exactly one in progress at a time, marked completed at each phase end - so phases render in the task banner. That list holds phase entries only for the whole task; keep step-level or per-file todos out of it. Name each phase in 3-6 words by its outcome, never by a command, file path, or flag, because the banner shows the title verbatim. Prose alone leaves the banner empty. 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 phase is a complete reviewable slice of behavior. Never split per file, per function, or per trivial step: over-splitting spends review turns instead of finishing the job. Add a phase only when a real review boundary, risky decision, or independent behavior separates the work. 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 phase completed, todo status, changed behavior/files, and remaining phases; 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}`;
65111
+ 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. Before editing, create and maintain the phase list with your provider's step-tracking tool (Claude Code: TaskCreate for each phase, then TaskUpdate for status, or TodoWrite when that is the tool offered; Codex: update_plan), never the MCP task board tools - one entry per phase in order, exactly one in progress at a time, marked completed at each phase end - so phases render in the task banner. That list holds phase entries only for the whole task; keep step-level or per-file todos out of it. Name each phase in 3-6 words by its outcome, never by a command, file path, or flag, because the banner shows the title verbatim. Prose alone leaves the banner empty. 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 phase is a complete reviewable slice of behavior. Never split per file, per function, or per trivial step: over-splitting spends review turns instead of finishing the job. Add a phase only when a real review boundary, risky decision, or independent behavior separates the work. 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 phase completed, todo status, changed behavior/files, and remaining phases; 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}`;
64190
65112
  const FUSION_WATCHER_TOOL_INSTRUCTIONS = `Supervisor tools belong to the p4-code MCP server: thread_watch_review, thread_watch_events, 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:
64191
65113
  ## Recommendation
64192
65114
  State the decision or correction in one sentence. For a phase review, say whether the phase is approved or needs changes.
@@ -64194,7 +65116,7 @@ State the decision or correction in one sentence. For a phase review, say whethe
64194
65116
  Give concise evidence, relevant file/line references, and any unresolved risk. Distinguish observed facts from assumptions; do not claim checks you did not run.
64195
65117
  ## Next step
64196
65118
  Give ordered, concrete actions for the builder, including the next phase and its completion condition. Preserve user requirements, constraints, and delivery authorization. Keep each section short; omit empty bullets and repeated status. Formatting does not authorize an otherwise unnecessary advice turn or replace an exact protocol response.`;
64197
- const FUSION_WATCHER_INSTRUCTIONS = `You are Fusion Supervisor (watcher) in an already-created native server pair. 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 direct the first actionable 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}`;
65119
+ 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 direct the first actionable 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}`;
64198
65120
  /**
64199
65121
  * The one-line stand-in for the full block on a message whose session already
64200
65122
  * carries the role instructions.
@@ -64214,6 +65136,13 @@ function fusionRoleInstructionsFor(role) {
64214
65136
  function fusionRoleReferenceLineFor(role) {
64215
65137
  return role === "implementer" ? FUSION_BUILDER_REFERENCE_LINE : FUSION_WATCHER_REFERENCE_LINE;
64216
65138
  }
65139
+ function fusionReviewPolicyInstructions(experiment) {
65140
+ if (!experiment) return "";
65141
+ const { policy } = experiment;
65142
+ return `[fusion-review-policy] Authoritative policy for this pair, overriding phase-boundary rules in session instructions: ${policy.mode}.
65143
+ ${policy.mode === "risk-and-final" ? "Builder: maintain your phase list, but continue across routine phase boundaries within the same turn. End only for a concrete unresolved judgment request, an external blocker, or final whole-task review after verification. Do not stop merely because one phase finished. Supervisor: after resolving a judgment request, direct the builder to continue through remaining routine phases and final verification; do not require another intermediate phase stop." : "Builder: retain one phase per turn. Supervisor: retain phase reviews."}
65144
+ At most ${policy.maxAutomaticReviews} automatic reviews and ${policy.maxCorrectionRounds} advice turns after review per human instruction. Initial direction is free. Current usage: ${experiment.automaticReviews} reviews, ${experiment.correctionRounds} correction rounds. Gate review wakes also consume reviews. Budget exhaustion pauses unresolved work; it never approves it. Human instruction to either thread resets the budget. Never send acknowledgment-only advice.`;
65145
+ }
64217
65146
  //#endregion
64218
65147
  //#region src/provider/GuardrailPrompts.ts
64219
65148
  /** Fresh-evidence gate adapted from superpowers' verification skill. */
@@ -65658,6 +66587,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
65658
66587
  context.inFlightTools.delete(index);
65659
66588
  }
65660
66589
  context.inFlightTools.clear();
66590
+ for (const toolUseId of context.subagentModels.keys()) if (!context.subagentTasks.has(toolUseId)) context.subagentModels.delete(toolUseId);
65661
66591
  for (const block of turnState.assistantTextBlockOrder) yield* completeAssistantTextBlock(context, block, {
65662
66592
  force: true,
65663
66593
  rawMethod: "claude/result",
@@ -65826,9 +66756,31 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
65826
66756
  discard: true
65827
66757
  });
65828
66758
  });
66759
+ const recordSubagentModel = Effect.fn("recordSubagentModel")(function* (context, toolUseId, model) {
66760
+ if (!toolUseId || !model || context.subagentModels.get(toolUseId) === model) return;
66761
+ const task = context.subagentTasks.get(toolUseId);
66762
+ if (!task && !Array.from(context.inFlightTools.values()).some((tool) => tool.itemId === toolUseId)) return;
66763
+ context.subagentModels.set(toolUseId, model);
66764
+ if (!task) return;
66765
+ yield* offerRuntimeEvent({
66766
+ ...yield* makeEventStamp(),
66767
+ type: "task.progress",
66768
+ provider: PROVIDER$6,
66769
+ threadId: context.session.threadId,
66770
+ ...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
66771
+ payload: {
66772
+ taskId: RuntimeTaskId.make(task.taskId),
66773
+ description: task.description,
66774
+ toolUseId,
66775
+ ...task.subagentType ? { subagentType: task.subagentType } : {},
66776
+ model
66777
+ }
66778
+ });
66779
+ });
65829
66780
  const handleStreamEvent = Effect.fn("handleStreamEvent")(function* (context, message) {
65830
66781
  if (message.type !== "stream_event") return;
65831
66782
  const { event } = message;
66783
+ if (event.type === "message_start") yield* recordSubagentModel(context, message.parent_tool_use_id, event.message.model);
65832
66784
  if (event.type === "message_delta") {
65833
66785
  if (message.parent_tool_use_id !== null && message.parent_tool_use_id !== void 0) return;
65834
66786
  const snapshot = normalizeClaudeActiveTokenUsage(event.usage, context.lastKnownContextWindow, context.lastKnownTotalProcessedTokens);
@@ -66149,10 +67101,12 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
66149
67101
  rawPayload: message
66150
67102
  });
66151
67103
  context.inFlightTools.delete(index);
67104
+ if (!context.subagentTasks.has(tool.itemId)) context.subagentModels.delete(tool.itemId);
66152
67105
  }
66153
67106
  });
66154
67107
  const handleAssistantMessage = Effect.fn("handleAssistantMessage")(function* (context, message) {
66155
67108
  if (message.type !== "assistant") return;
67109
+ yield* recordSubagentModel(context, message.parent_tool_use_id, message.message.model);
66156
67110
  if (!context.turnState) {
66157
67111
  const turnId = TurnId.make(yield* randomUUIDv4);
66158
67112
  const startedAt = yield* nowIso;
@@ -66259,6 +67213,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
66259
67213
  }
66260
67214
  switch (message.subtype) {
66261
67215
  case "init":
67216
+ context.currentApiModelId = message.model || context.currentApiModelId;
66262
67217
  yield* offerRuntimeEvent({
66263
67218
  ...base,
66264
67219
  type: "session.configured",
@@ -66329,7 +67284,12 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
66329
67284
  return;
66330
67285
  case "task_started": {
66331
67286
  const requestedModel = readString((message.tool_use_id ? Array.from(context.inFlightTools.values()).find((tool) => tool.itemId === message.tool_use_id) : void 0)?.input.model);
66332
- const model = requestedModel === "inherit" ? context.currentApiModelId : requestedModel;
67287
+ const model = (message.tool_use_id ? context.subagentModels.get(message.tool_use_id) : void 0) ?? (requestedModel === "inherit" ? context.currentApiModelId : requestedModel);
67288
+ if (message.tool_use_id) context.subagentTasks.set(message.tool_use_id, {
67289
+ taskId: message.task_id,
67290
+ description: message.description,
67291
+ ...message.subagent_type ? { subagentType: message.subagent_type } : {}
67292
+ });
66333
67293
  yield* offerRuntimeEvent({
66334
67294
  ...base,
66335
67295
  type: "task.started",
@@ -66380,6 +67340,11 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
66380
67340
  ...message.tool_use_id ? { toolUseId: message.tool_use_id } : {}
66381
67341
  }
66382
67342
  });
67343
+ for (const [toolUseId, task] of context.subagentTasks) if (toolUseId === message.tool_use_id || task.taskId === message.task_id) {
67344
+ context.subagentTasks.delete(toolUseId);
67345
+ context.subagentModels.delete(toolUseId);
67346
+ }
67347
+ if (message.tool_use_id) context.subagentModels.delete(message.tool_use_id);
66383
67348
  return;
66384
67349
  case "files_persisted":
66385
67350
  yield* offerRuntimeEvent({
@@ -67080,6 +68045,8 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
67080
68045
  turns: [],
67081
68046
  inFlightTools,
67082
68047
  claudeTasks,
68048
+ subagentTasks: /* @__PURE__ */ new Map(),
68049
+ subagentModels: /* @__PURE__ */ new Map(),
67083
68050
  turnState: void 0,
67084
68051
  pendingTurnRequest: void 0,
67085
68052
  steeredMessages: /* @__PURE__ */ new Map(),
@@ -85454,7 +86421,7 @@ const makeTerminationError = (handle) => Effect.match(handle.exitCode, {
85454
86421
  //#endregion
85455
86422
  //#region ../../packages/effect-codex-app-server/src/client.ts
85456
86423
  var CodexAppServerClient = class extends Context.Service()("effect-codex-app-server/client/CodexAppServerClient") {};
85457
- const make$67 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(function* (stdio, options = {}, terminationError) {
86424
+ const make$68 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(function* (stdio, options = {}, terminationError) {
85458
86425
  const requestHandlers = /* @__PURE__ */ new Map();
85459
86426
  const notificationHandlers = /* @__PURE__ */ new Map();
85460
86427
  let unknownRequestHandler;
@@ -85521,7 +86488,7 @@ const make$67 = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(f
85521
86488
  const layerChildProcess = (handle, options = {}) => Layer.effect(CodexAppServerClient, makeChildProcessClient(handle, options));
85522
86489
  const makeChildProcessClient = Effect.fn("effect-codex-app-server/CodexAppServerClient.makeChildProcessClient")(function* (handle, options) {
85523
86490
  yield* Stream.runDrain(handle.stderr).pipe(Effect.ignore, Effect.forkScoped);
85524
- return yield* make$67(makeChildStdio(handle), options, makeTerminationError(handle));
86491
+ return yield* make$68(makeChildStdio(handle), options, makeTerminationError(handle));
85525
86492
  });
85526
86493
  const resolveCodexLaunchArgs = (launchArgs, environment = process.env) => environment["P4CODE_CODEX_LAUNCH_ARGS"]?.trim() || launchArgs?.trim() || "";
85527
86494
  const codexLaunchArgv = (launchArgs) => tokenizeCliArgs(launchArgs);
@@ -89365,7 +90332,7 @@ function buildCursorDiscoveredModelsFromAvailableModelsResponse(response) {
89365
90332
  }
89366
90333
  const makeCursorAcpProbeRuntime = (cursorSettings, environment) => Effect.gen(function* () {
89367
90334
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
89368
- const acpContext = yield* Layer.build(layer$60({
90335
+ const acpContext = yield* Layer.build(layer$61({
89369
90336
  spawn: {
89370
90337
  command: cursorSettings.binaryPath,
89371
90338
  args: [...cursorSettings.apiEndpoint ? ["-e", cursorSettings.apiEndpoint] : [], "acp"],
@@ -89815,7 +90782,7 @@ function buildCursorAcpSpawnInput(cursorSettings, cwd, environment) {
89815
90782
  };
89816
90783
  }
89817
90784
  const makeCursorAcpRuntime = (input) => Effect.gen(function* () {
89818
- const acpContext = yield* Layer.build(layer$60({
90785
+ const acpContext = yield* Layer.build(layer$61({
89819
90786
  ...input,
89820
90787
  spawn: buildCursorAcpSpawnInput(input.cursorSettings, input.cwd, input.environment),
89821
90788
  authMethodId: "cursor_login",
@@ -91008,7 +91975,7 @@ function resolveGrokAuthMethodId(environment) {
91008
91975
  return environment?.[GROK_API_KEY_ENV]?.trim() ? GROK_AUTH_METHOD_API_KEY : GROK_AUTH_METHOD_CACHED_TOKEN;
91009
91976
  }
91010
91977
  const makeGrokAcpRuntime = (input) => Effect.gen(function* () {
91011
- const acpContext = yield* Layer.build(layer$60({
91978
+ const acpContext = yield* Layer.build(layer$61({
91012
91979
  ...input,
91013
91980
  spawn: buildGrokAcpSpawnInput(input.grokSettings, input.cwd, input.environment),
91014
91981
  authMethodId: resolveGrokAuthMethodId(input.environment)
@@ -94467,7 +95434,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
94467
95434
  };
94468
95435
  });
94469
95436
  var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
94470
- const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$118));
95437
+ const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$119));
94471
95438
  //#endregion
94472
95439
  //#region src/textGeneration/OpenCodeTextGeneration.ts
94473
95440
  const OPENCODE_TEXT_GENERATION_IDLE_TTL = "30 seconds";
@@ -97057,8 +98024,8 @@ const makeWithReader = (api) => Effect.gen(function* () {
97057
98024
  }))
97058
98025
  };
97059
98026
  });
97060
- const make$66 = makeCliproxyApi.pipe(Effect.flatMap(makeWithReader));
97061
- const layer$57 = Layer.effect(UsageLimitSources, make$66);
98027
+ const make$67 = makeCliproxyApi.pipe(Effect.flatMap(makeWithReader));
98028
+ const layer$58 = Layer.effect(UsageLimitSources, make$67);
97062
98029
  //#endregion
97063
98030
  //#region src/resourceTelemetry/HostResources.ts
97064
98031
  const CPU_SAMPLE_INTERVAL = "200 millis";
@@ -97093,7 +98060,7 @@ function darwinAvailableMemory(output) {
97093
98060
  const available = (Number(free) + Number(inactive) + Number(speculative)) * Number(pageSize);
97094
98061
  return Number.isSafeInteger(available) && Number(pageSize) > 0 ? available : null;
97095
98062
  }
97096
- const make$65 = Effect.fn("makeHostResources")(function* () {
98063
+ const make$66 = Effect.fn("makeHostResources")(function* () {
97097
98064
  const fs = yield* FileSystem.FileSystem;
97098
98065
  const platform = yield* HostProcessPlatform;
97099
98066
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
@@ -97127,7 +98094,7 @@ const make$65 = Effect.fn("makeHostResources")(function* () {
97127
98094
  });
97128
98095
  return HostResources.of({ read: Cache.get(cache, "host") });
97129
98096
  });
97130
- const layer$56 = Layer.effect(HostResources, make$65());
98097
+ const layer$57 = Layer.effect(HostResources, make$66());
97131
98098
  //#endregion
97132
98099
  //#region src/resourceTelemetry/DesktopTelemetryReceiver.ts
97133
98100
  const INITIAL_SAMPLE_DEADLINE_MS = 9e4;
@@ -97262,7 +98229,7 @@ function requireDesktopTelemetryWriteProgress(fd, remainingBytes, written) {
97262
98229
  }));
97263
98230
  }
97264
98231
  /** @public Service construction is part of the canonical Effect module API. */
97265
- const make$64 = Effect.fn("resourceTelemetry.desktopTelemetryReceiver.make")(function* () {
98232
+ const make$65 = Effect.fn("resourceTelemetry.desktopTelemetryReceiver.make")(function* () {
97266
98233
  const serverConfig = yield* ServerConfig$1;
97267
98234
  const hasDescriptors = serverConfig.mode === "desktop" && serverConfig.desktopTelemetryFd === 4 && serverConfig.desktopTelemetryControlFd === 5;
97268
98235
  const config = {
@@ -97419,7 +98386,7 @@ const make$64 = Effect.fn("resourceTelemetry.desktopTelemetryReceiver.make")(fun
97419
98386
  setDiagnosticsDemand
97420
98387
  });
97421
98388
  });
97422
- const layer$55 = Layer.effect(DesktopTelemetryReceiver, make$64());
98389
+ const layer$56 = Layer.effect(DesktopTelemetryReceiver, make$65());
97423
98390
  //#endregion
97424
98391
  //#region src/resourceTelemetry/Model.ts
97425
98392
  const MAX_DELTA_INTERVAL_MS = 3e4;
@@ -97844,7 +98811,7 @@ function resourceMonitorRustTarget(platform, architecture, linuxLibc) {
97844
98811
  }
97845
98812
  if (platform === "win32") return architecture === "arm64" ? "aarch64-pc-windows-msvc" : architecture === "x64" ? "x86_64-pc-windows-msvc" : void 0;
97846
98813
  }
97847
- const make$63 = Effect.fn("resourceTelemetry.resourceMonitorBinary.make")(function* () {
98814
+ const make$64 = Effect.fn("resourceTelemetry.resourceMonitorBinary.make")(function* () {
97848
98815
  const fileSystem = yield* FileSystem.FileSystem;
97849
98816
  const path = yield* Path$1.Path;
97850
98817
  const platform = yield* HostProcessPlatform;
@@ -97890,7 +98857,7 @@ const make$63 = Effect.fn("resourceTelemetry.resourceMonitorBinary.make")(functi
97890
98857
  });
97891
98858
  return ResourceMonitorBinary.of({ resolve });
97892
98859
  });
97893
- const layer$54 = Layer.effect(ResourceMonitorBinary, make$63());
98860
+ const layer$55 = Layer.effect(ResourceMonitorBinary, make$64());
97894
98861
  //#endregion
97895
98862
  //#region src/resourceTelemetry/NativeTelemetryClient.ts
97896
98863
  const SAMPLE_INTERVAL_MS$1 = 1e3;
@@ -98040,7 +99007,7 @@ function canCommandNativeTelemetrySidecar(status, hasHandle) {
98040
99007
  return hasHandle && (status === "healthy" || status === "degraded");
98041
99008
  }
98042
99009
  /** @public Service construction is part of the canonical Effect module API. */
98043
- const make$62 = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(function* () {
99010
+ const make$63 = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(function* () {
98044
99011
  const binary = yield* ResourceMonitorBinary;
98045
99012
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
98046
99013
  const crypto = yield* Crypto.Crypto;
@@ -98451,7 +99418,7 @@ const make$62 = Effect.fn("resourceTelemetry.nativeTelemetryClient.make")(functi
98451
99418
  })
98452
99419
  });
98453
99420
  });
98454
- const layer$53 = Layer.effect(NativeTelemetryClient, make$62());
99421
+ const layer$54 = Layer.effect(NativeTelemetryClient, make$63());
98455
99422
  //#endregion
98456
99423
  //#region src/resourceTelemetry/ResourceAttribution.ts
98457
99424
  var ResourceAttribution = class extends Context.Service()("@p4code/cli/resourceTelemetry/ResourceAttribution") {};
@@ -98463,7 +99430,7 @@ function nonNegativeInteger(value, fallback) {
98463
99430
  if (!Number.isFinite(value)) return 0;
98464
99431
  return Math.max(0, Math.round(value));
98465
99432
  }
98466
- const make$61 = Effect.fn("resourceTelemetry.resourceAttribution.make")(function* () {
99433
+ const make$62 = Effect.fn("resourceTelemetry.resourceAttribution.make")(function* () {
98467
99434
  const entries = yield* Ref.make(/* @__PURE__ */ new Map());
98468
99435
  const record = (input) => Ref.update(entries, (current) => {
98469
99436
  const next = new Map(current);
@@ -98489,7 +99456,7 @@ const make$61 = Effect.fn("resourceTelemetry.resourceAttribution.make")(function
98489
99456
  })
98490
99457
  });
98491
99458
  });
98492
- const layer$52 = Layer.effect(ResourceAttribution, make$61());
99459
+ const layer$53 = Layer.effect(ResourceAttribution, make$62());
98493
99460
  //#endregion
98494
99461
  //#region src/resourceTelemetry/ResourceTelemetryHistory.ts
98495
99462
  const MAX_HISTORY_WINDOW_MS = 60 * 6e4;
@@ -98712,7 +99679,7 @@ function buildHealth(input) {
98712
99679
  };
98713
99680
  }
98714
99681
  /** @public Service construction is part of the canonical Effect module API. */
98715
- const make$60 = Effect.fn("resourceTelemetry.resourceTelemetry.make")(function* () {
99682
+ const make$61 = Effect.fn("resourceTelemetry.resourceTelemetry.make")(function* () {
98716
99683
  const nativeClient = yield* NativeTelemetryClient;
98717
99684
  const desktopReceiver = yield* DesktopTelemetryReceiver;
98718
99685
  const attribution = yield* ResourceAttribution;
@@ -98964,7 +99931,7 @@ const make$60 = Effect.fn("resourceTelemetry.resourceTelemetry.make")(function*
98964
99931
  })))
98965
99932
  });
98966
99933
  });
98967
- const layer$51 = Layer.effect(ResourceTelemetry, make$60());
99934
+ const layer$52 = Layer.effect(ResourceTelemetry, make$61());
98968
99935
  //#endregion
98969
99936
  //#region src/device/DeviceInspection.ts
98970
99937
  const READ_TIMEOUT_MS = 1e4;
@@ -102119,7 +103086,7 @@ const classifyNonZeroExit = (command, stderr) => {
102119
103086
  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";
102120
103087
  return "command-failed";
102121
103088
  };
102122
- const make$59 = Effect.gen(function* () {
103089
+ const make$60 = Effect.gen(function* () {
102123
103090
  const processRunner = yield* ProcessRunner;
102124
103091
  const run = Effect.fn("VcsProcess.run")(function* (input) {
102125
103092
  const baseError = {
@@ -102178,7 +103145,7 @@ const make$59 = Effect.gen(function* () {
102178
103145
  });
102179
103146
  return VcsProcess.of({ run });
102180
103147
  });
102181
- const layer$50 = Layer.effect(VcsProcess, make$59).pipe(Layer.provide(layer$81));
103148
+ const layer$51 = Layer.effect(VcsProcess, make$60).pipe(Layer.provide(layer$82));
102182
103149
  //#endregion
102183
103150
  //#region src/vcs/VcsDriver.ts
102184
103151
  var VcsDriver = class extends Context.Service()("@p4code/cli/vcs/VcsDriver") {};
@@ -102629,12 +103596,12 @@ const makeVcsDriver = Effect.gen(function* () {
102629
103596
  const driver = yield* makeVcsDriverShape();
102630
103597
  return VcsDriver.of(driver);
102631
103598
  });
102632
- const make$58 = Effect.gen(function* () {
103599
+ const make$59 = Effect.gen(function* () {
102633
103600
  const git = yield* makeGitVcsDriverCore();
102634
103601
  return GitVcsDriver.of(git);
102635
103602
  });
102636
103603
  Layer.effect(VcsDriver, makeVcsDriver);
102637
- const layer$49 = Layer.effect(GitVcsDriver, make$58);
103604
+ const layer$50 = Layer.effect(GitVcsDriver, make$59);
102638
103605
  //#endregion
102639
103606
  //#region src/vcs/VcsProjectConfig.ts
102640
103607
  const ProjectVcsConfigJson = fromLenientJson(Schema$1.Struct({
@@ -102666,7 +103633,7 @@ const logVcsProjectConfigError = (error) => Effect.logWarning(error).pipe(Effect
102666
103633
  configPath: error.configPath,
102667
103634
  errorTag: error._tag
102668
103635
  }));
102669
- const make$57 = Effect.gen(function* () {
103636
+ const make$58 = Effect.gen(function* () {
102670
103637
  const fileSystem = yield* FileSystem.FileSystem;
102671
103638
  const path = yield* Path$1.Path;
102672
103639
  const findConfigPath = Effect.fn("VcsProjectConfig.findConfigPath")(function* (cwd) {
@@ -102707,7 +103674,7 @@ const make$57 = Effect.gen(function* () {
102707
103674
  });
102708
103675
  return VcsProjectConfig.of({ resolveKind });
102709
103676
  });
102710
- const layer$48 = Layer.effect(VcsProjectConfig, make$57);
103677
+ const layer$49 = Layer.effect(VcsProjectConfig, make$58);
102711
103678
  //#endregion
102712
103679
  //#region src/vcs/VcsDriverRegistry.ts
102713
103680
  const DETECTION_CACHE_CAPACITY = 2048;
@@ -102727,7 +103694,7 @@ function parseDetectionCacheKey(key) {
102727
103694
  cwd: key.slice(separatorIndex + 1)
102728
103695
  };
102729
103696
  }
102730
- const make$56 = Effect.gen(function* () {
103697
+ const make$57 = Effect.gen(function* () {
102731
103698
  const projectConfig = yield* VcsProjectConfig;
102732
103699
  const git = yield* makeVcsDriver;
102733
103700
  const drivers = { git };
@@ -102784,7 +103751,7 @@ const make$56 = Effect.gen(function* () {
102784
103751
  resolve
102785
103752
  });
102786
103753
  });
102787
- const layer$47 = Layer.effect(VcsDriverRegistry, make$56).pipe(Layer.provide(layer$48));
103754
+ const layer$48 = Layer.effect(VcsDriverRegistry, make$57).pipe(Layer.provide(layer$49));
102788
103755
  //#endregion
102789
103756
  //#region src/checkpointing/CheckpointStore.ts
102790
103757
  /**
@@ -102804,7 +103771,7 @@ const layer$47 = Layer.effect(VcsDriverRegistry, make$56).pipe(Layer.provide(lay
102804
103771
  */
102805
103772
  /** Service tag for checkpoint persistence and restore operations. */
102806
103773
  var CheckpointStore = class extends Context.Service()("@p4code/cli/checkpointing/CheckpointStore") {};
102807
- const make$55 = Effect.gen(function* () {
103774
+ const make$56 = Effect.gen(function* () {
102808
103775
  const vcsRegistry = yield* VcsDriverRegistry;
102809
103776
  const resolveCheckpoints = Effect.fn("CheckpointStore.resolveCheckpoints")(function* (operation, cwd) {
102810
103777
  const handle = yield* vcsRegistry.resolve({ cwd });
@@ -102843,7 +103810,7 @@ const make$55 = Effect.gen(function* () {
102843
103810
  deleteCheckpointRefs
102844
103811
  });
102845
103812
  });
102846
- const layer$46 = Layer.effect(CheckpointStore, make$55);
103813
+ const layer$47 = Layer.effect(CheckpointStore, make$56);
102847
103814
  //#endregion
102848
103815
  //#region src/checkpointing/CheckpointDiffQuery.ts
102849
103816
  /**
@@ -102865,7 +103832,7 @@ function buildTurnDiffResult(input, diff) {
102865
103832
  diff
102866
103833
  };
102867
103834
  }
102868
- const make$54 = Effect.gen(function* () {
103835
+ const make$55 = Effect.gen(function* () {
102869
103836
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
102870
103837
  const checkpointStore = yield* CheckpointStore;
102871
103838
  const threadActivities = yield* ProjectionThreadActivityRepository;
@@ -103036,7 +104003,7 @@ const make$54 = Effect.gen(function* () {
103036
104003
  getFullThreadDiff
103037
104004
  });
103038
104005
  });
103039
- const layer$45 = Layer.effect(CheckpointDiffQuery, make$54);
104006
+ const layer$46 = Layer.effect(CheckpointDiffQuery, make$55);
103040
104007
  //#endregion
103041
104008
  //#region src/orchestration/ThreadLiveEventCoalescer.ts
103042
104009
  const COALESCE_WINDOW = Duration.millis(50);
@@ -103581,7 +104548,7 @@ const serversEqual = (left, right) => {
103581
104548
  }
103582
104549
  return true;
103583
104550
  };
103584
- const make$53 = Effect.gen(function* PortDiscoveryMake() {
104551
+ const make$54 = Effect.gen(function* PortDiscoveryMake() {
103585
104552
  const net = yield* NetService;
103586
104553
  const processRunner = yield* ProcessRunner;
103587
104554
  const hostPlatform = yield* HostProcessPlatform;
@@ -103732,7 +104699,7 @@ const make$53 = Effect.gen(function* PortDiscoveryMake() {
103732
104699
  unregisterTerminal
103733
104700
  });
103734
104701
  }).pipe(Effect.withSpan("PortDiscovery.make"));
103735
- const layer$44 = Layer.effect(PortDiscovery, make$53);
104702
+ const layer$45 = Layer.effect(PortDiscovery, make$54);
103736
104703
  //#endregion
103737
104704
  //#region src/terminal/Manager.ts
103738
104705
  /**
@@ -104410,7 +105377,7 @@ function normalizedRuntimeEnv(env) {
104410
105377
  if (entries.length === 0) return null;
104411
105378
  return Object.fromEntries(entries.toSorted(([left], [right]) => left.localeCompare(right)));
104412
105379
  }
104413
- const make$52 = Effect.fn("TerminalManager.make")(function* () {
105380
+ const make$53 = Effect.fn("TerminalManager.make")(function* () {
104414
105381
  const { terminalLogsDir } = yield* ServerConfig$1;
104415
105382
  const ptyAdapter = yield* PtyAdapter;
104416
105383
  const portDiscovery = yield* PortDiscovery;
@@ -105372,7 +106339,7 @@ const makeWithOptions$1 = Effect.fn("TerminalManager.makeWithOptions")(function*
105372
106339
  subscribeMetadata
105373
106340
  });
105374
106341
  });
105375
- const layer$43 = Layer.effect(TerminalManager, make$52()).pipe(Layer.provide(layer$81));
106342
+ const layer$44 = Layer.effect(TerminalManager, make$53()).pipe(Layer.provide(layer$82));
105376
106343
  //#endregion
105377
106344
  //#region src/project/ProjectSetupScriptRunner.ts
105378
106345
  var ProjectSetupScriptOperationError = class extends Schema$1.TaggedErrorClass()("ProjectSetupScriptOperationError", {
@@ -105404,7 +106371,7 @@ var ProjectSetupScriptProjectNotFoundError = class extends Schema$1.TaggedErrorC
105404
106371
  };
105405
106372
  Schema$1.Union([ProjectSetupScriptOperationError, ProjectSetupScriptProjectNotFoundError]);
105406
106373
  var ProjectSetupScriptRunner = class extends Context.Service()("@p4code/cli/project/ProjectSetupScriptRunner") {};
105407
- const make$51 = Effect.gen(function* () {
106374
+ const make$52 = Effect.gen(function* () {
105408
106375
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
105409
106376
  const terminalManager = yield* TerminalManager;
105410
106377
  const settingsService = yield* Effect.serviceOption(ServerSettingsService);
@@ -105467,7 +106434,7 @@ const make$51 = Effect.gen(function* () {
105467
106434
  });
105468
106435
  return ProjectSetupScriptRunner.of({ runForThread });
105469
106436
  });
105470
- const layer$42 = Layer.effect(ProjectSetupScriptRunner, make$51);
106437
+ const layer$43 = Layer.effect(ProjectSetupScriptRunner, make$52);
105471
106438
  //#endregion
105472
106439
  //#region src/sourceControl/azureDevOpsPullRequests.ts
105473
106440
  const AzureDevOpsPullRequestSchema = Schema$1.Struct({
@@ -105795,7 +106762,7 @@ function decodeAzureDevOpsJson(raw, schema, operation, cwd) {
105795
106762
  cause
105796
106763
  })));
105797
106764
  }
105798
- const make$50 = Effect.gen(function* () {
106765
+ const make$51 = Effect.gen(function* () {
105799
106766
  const process = yield* VcsProcess;
105800
106767
  const execute = (input) => process.run({
105801
106768
  operation: "AzureDevOpsCli.execute",
@@ -105937,7 +106904,7 @@ const make$50 = Effect.gen(function* () {
105937
106904
  }).pipe(Effect.asVoid)
105938
106905
  });
105939
106906
  });
105940
- const layer$41 = Layer.effect(AzureDevOpsCli, make$50);
106907
+ const layer$42 = Layer.effect(AzureDevOpsCli, make$51);
105941
106908
  //#endregion
105942
106909
  //#region src/sourceControl/SourceControlProviderDiscovery.ts
105943
106910
  function firstNonEmptyLine(text) {
@@ -106141,7 +107108,7 @@ function toChangeRequest$5(summary) {
106141
107108
  isCrossRepository: false
106142
107109
  };
106143
107110
  }
106144
- const make$49 = Effect.gen(function* () {
107111
+ const make$50 = Effect.gen(function* () {
106145
107112
  const azure = yield* AzureDevOpsCli;
106146
107113
  return SourceControlProvider.of({
106147
107114
  kind: "azure-devops",
@@ -106234,7 +107201,7 @@ const make$49 = Effect.gen(function* () {
106234
107201
  })))
106235
107202
  });
106236
107203
  });
106237
- Layer.effect(SourceControlProvider, make$49);
107204
+ Layer.effect(SourceControlProvider, make$50);
106238
107205
  //#endregion
106239
107206
  //#region src/sourceControl/bitbucketPullRequests.ts
106240
107207
  const BitbucketRepositoryRefSchema = Schema$1.Struct({
@@ -106611,7 +107578,7 @@ function responseError(operation, response) {
106611
107578
  responseBodyLength: collected.text.length
106612
107579
  }))));
106613
107580
  }
106614
- const make$48 = Effect.gen(function* () {
107581
+ const make$49 = Effect.gen(function* () {
106615
107582
  const config = yield* BitbucketApiEnvConfig;
106616
107583
  const httpClient = yield* HttpClient.HttpClient;
106617
107584
  const fileSystem = yield* FileSystem.FileSystem;
@@ -106827,7 +107794,7 @@ const make$48 = Effect.gen(function* () {
106827
107794
  })))
106828
107795
  });
106829
107796
  });
106830
- const layer$39 = Layer.effect(BitbucketApi, make$48);
107797
+ const layer$40 = Layer.effect(BitbucketApi, make$49);
106831
107798
  //#endregion
106832
107799
  //#region src/sourceControl/BitbucketSourceControlProvider.ts
106833
107800
  function toChangeRequest$4(summary) {
@@ -106845,7 +107812,7 @@ function toChangeRequest$4(summary) {
106845
107812
  ...summary.headRepositoryOwnerLogin !== void 0 ? { headRepositoryOwnerLogin: summary.headRepositoryOwnerLogin } : {}
106846
107813
  };
106847
107814
  }
106848
- const make$47 = Effect.gen(function* () {
107815
+ const make$48 = Effect.gen(function* () {
106849
107816
  const bitbucket = yield* BitbucketApi;
106850
107817
  return SourceControlProvider.of({
106851
107818
  kind: "bitbucket",
@@ -106936,7 +107903,7 @@ const make$47 = Effect.gen(function* () {
106936
107903
  })))
106937
107904
  });
106938
107905
  });
106939
- Layer.effect(SourceControlProvider, make$47);
107906
+ Layer.effect(SourceControlProvider, make$48);
106940
107907
  const makeDiscovery = Effect.gen(function* () {
106941
107908
  return {
106942
107909
  type: "api",
@@ -107184,7 +108151,7 @@ function deriveRepositoryCloneUrlsFromCreateOutput(stdout, repository) {
107184
108151
  sshUrl: `git@${fallbackHost}:${repository}.git`
107185
108152
  };
107186
108153
  }
107187
- const make$46 = Effect.gen(function* () {
108154
+ const make$47 = Effect.gen(function* () {
107188
108155
  const process = yield* VcsProcess;
107189
108156
  const execute = (input) => process.run({
107190
108157
  operation: "GitHubCli.execute",
@@ -107302,7 +108269,7 @@ const make$46 = Effect.gen(function* () {
107302
108269
  }).pipe(Effect.asVoid)
107303
108270
  });
107304
108271
  });
107305
- const layer$37 = Layer.effect(GitHubCli, make$46);
108272
+ const layer$38 = Layer.effect(GitHubCli, make$47);
107306
108273
  //#endregion
107307
108274
  //#region src/sourceControl/gitHubAuthStatus.ts
107308
108275
  const GitHubAuthStatusAccountSchema = Schema$1.Struct({
@@ -107404,7 +108371,7 @@ const discovery$1 = {
107404
108371
  parseAuth: parseGitHubAuth,
107405
108372
  installHint: "Install the GitHub command-line tool (`gh`) via https://cli.github.com/ or your package manager (for example `brew install gh`)."
107406
108373
  };
107407
- const make$45 = Effect.gen(function* () {
108374
+ const make$46 = Effect.gen(function* () {
107408
108375
  const github = yield* GitHubCli;
107409
108376
  const listChangeRequests = (input) => {
107410
108377
  if (input.state === "open") return github.listOpenPullRequests({
@@ -107520,7 +108487,7 @@ const make$45 = Effect.gen(function* () {
107520
108487
  })))
107521
108488
  });
107522
108489
  });
107523
- Layer.effect(SourceControlProvider, make$45);
108490
+ Layer.effect(SourceControlProvider, make$46);
107524
108491
  //#endregion
107525
108492
  //#region src/sourceControl/gitLabMergeRequests.ts
107526
108493
  const GitLabProjectReferenceSchema = Schema$1.Struct({
@@ -107839,7 +108806,7 @@ function parseRepositoryPath(repository) {
107839
108806
  projectPath
107840
108807
  };
107841
108808
  }
107842
- const make$44 = Effect.gen(function* () {
108809
+ const make$45 = Effect.gen(function* () {
107843
108810
  const process = yield* VcsProcess;
107844
108811
  const run = (input, mapError) => process.run({
107845
108812
  operation: "GitLabCli.execute",
@@ -107990,7 +108957,7 @@ const make$44 = Effect.gen(function* () {
107990
108957
  }).pipe(Effect.asVoid)
107991
108958
  });
107992
108959
  });
107993
- const layer$35 = Layer.effect(GitLabCli, make$44);
108960
+ const layer$36 = Layer.effect(GitLabCli, make$45);
107994
108961
  //#endregion
107995
108962
  //#region src/sourceControl/gitLabAuthStatus.ts
107996
108963
  const HOST_LINE_PATTERN = /^(?:[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?|\[[a-f0-9:.]+\])(?::\d+)?$/iu;
@@ -108088,7 +109055,7 @@ const discovery = {
108088
109055
  refineUnknownRemote: refineUnknownGitLabRemote,
108089
109056
  installHint: "Install the GitLab command-line tool (`glab`) from https://gitlab.com/gitlab-org/cli or your package manager (for example `brew install glab`)."
108090
109057
  };
108091
- const make$43 = Effect.gen(function* () {
109058
+ const make$44 = Effect.gen(function* () {
108092
109059
  const gitlab = yield* GitLabCli;
108093
109060
  return SourceControlProvider.of({
108094
109061
  kind: "gitlab",
@@ -108177,7 +109144,7 @@ const make$43 = Effect.gen(function* () {
108177
109144
  })))
108178
109145
  });
108179
109146
  });
108180
- Layer.effect(SourceControlProvider, make$43);
109147
+ Layer.effect(SourceControlProvider, make$44);
108181
109148
  //#endregion
108182
109149
  //#region src/sourceControl/SourceControlProviderRegistry.ts
108183
109150
  const PROVIDER_DETECTION_CACHE_CAPACITY = 2048;
@@ -108333,12 +109300,12 @@ const makeWithProviders = Effect.fn("makeSourceControlProviderRegistryWithProvid
108333
109300
  })), { concurrency: "unbounded" })
108334
109301
  });
108335
109302
  });
108336
- const make$42 = Effect.gen(function* () {
108337
- const github = yield* make$45;
108338
- const gitlab = yield* make$43;
108339
- const bitbucket = yield* make$47;
109303
+ const make$43 = Effect.gen(function* () {
109304
+ const github = yield* make$46;
109305
+ const gitlab = yield* make$44;
109306
+ const bitbucket = yield* make$48;
108340
109307
  const bitbucketDiscovery = yield* makeDiscovery;
108341
- const azureDevOps = yield* make$49;
109308
+ const azureDevOps = yield* make$50;
108342
109309
  return yield* makeWithProviders([
108343
109310
  {
108344
109311
  kind: "github",
@@ -108362,7 +109329,7 @@ const make$42 = Effect.gen(function* () {
108362
109329
  }
108363
109330
  ]);
108364
109331
  });
108365
- const layer$33 = Layer.effect(SourceControlProviderRegistry, make$42);
109332
+ const layer$34 = Layer.effect(SourceControlProviderRegistry, make$43);
108366
109333
  //#endregion
108367
109334
  //#region src/sourceControl/PrTemplateDetection.ts
108368
109335
  const TEMPLATE_MAX_BYTES = 8e3;
@@ -108733,7 +109700,7 @@ function toPullRequestHeadRemoteInfo(pr) {
108733
109700
  ...pr.headRepositoryOwnerLogin !== void 0 ? { headRepositoryOwnerLogin: pr.headRepositoryOwnerLogin } : {}
108734
109701
  };
108735
109702
  }
108736
- const make$41 = Effect.gen(function* () {
109703
+ const make$42 = Effect.gen(function* () {
108737
109704
  const gitCore = yield* GitVcsDriver;
108738
109705
  const sourceControlProviders = yield* SourceControlProviderRegistry;
108739
109706
  const textGeneration = yield* TextGeneration;
@@ -109707,7 +110674,7 @@ const make$41 = Effect.gen(function* () {
109707
110674
  runStackedAction
109708
110675
  });
109709
110676
  });
109710
- const layer$32 = Layer.effect(GitManager, make$41);
110677
+ const layer$33 = Layer.effect(GitManager, make$42);
109711
110678
  //#endregion
109712
110679
  //#region src/git/GitWorkflowService.ts
109713
110680
  var GitWorkflowService = class extends Context.Service()("@p4code/cli/git/GitWorkflowService") {};
@@ -109744,7 +110711,7 @@ function nonRepositoryListRefs() {
109744
110711
  totalCount: 0
109745
110712
  };
109746
110713
  }
109747
- const make$40 = Effect.gen(function* () {
110714
+ const make$41 = Effect.gen(function* () {
109748
110715
  const registry = yield* VcsDriverRegistry;
109749
110716
  const git = yield* GitVcsDriver;
109750
110717
  const gitManager = yield* GitManager;
@@ -109830,7 +110797,7 @@ const make$40 = Effect.gen(function* () {
109830
110797
  renameBranch: (input) => ensureGit("GitWorkflowService.renameBranch", input.cwd).pipe(Effect.andThen(git.renameBranch(input)))
109831
110798
  });
109832
110799
  });
109833
- const layer$31 = Layer.effect(GitWorkflowService, make$40);
110800
+ const layer$32 = Layer.effect(GitWorkflowService, make$41);
109834
110801
  //#endregion
109835
110802
  //#region src/pullRequest/PullRequestProvider.ts
109836
110803
  /**
@@ -110191,7 +111158,7 @@ function isReviewerName(value) {
110191
111158
  const name = value.trim();
110192
111159
  return name.length > 0 && !name.startsWith("-");
110193
111160
  }
110194
- const make$39 = Effect.gen(function* () {
111161
+ const make$40 = Effect.gen(function* () {
110195
111162
  const azure = yield* AzureDevOpsCli;
110196
111163
  const detectArgs = ["--detect", "true"];
110197
111164
  const executeJson = (input) => azure.execute({
@@ -110391,7 +111358,7 @@ const make$39 = Effect.gen(function* () {
110391
111358
  }).pipe(Effect.asVoid)
110392
111359
  });
110393
111360
  });
110394
- const layer$30 = Layer.effect(AzureDevOpsPullRequestCli, make$39);
111361
+ const layer$31 = Layer.effect(AzureDevOpsPullRequestCli, make$40);
110395
111362
  //#endregion
110396
111363
  //#region src/pullRequest/AzureDevOpsPullRequestProvider.ts
110397
111364
  const CAPABILITIES$3 = {
@@ -110466,7 +111433,7 @@ function toChangeRequest$1(pullRequest) {
110466
111433
  labels: []
110467
111434
  };
110468
111435
  }
110469
- const make$38 = Effect.gen(function* () {
111436
+ const make$39 = Effect.gen(function* () {
110470
111437
  const cli = yield* AzureDevOpsPullRequestCli;
110471
111438
  const fail = (operation) => (error) => new PullRequestProviderError({
110472
111439
  provider: "azure-devops",
@@ -111206,7 +112173,7 @@ function mergeStrategy(method) {
111206
112173
  default: return "merge_commit";
111207
112174
  }
111208
112175
  }
111209
- const make$37 = Effect.gen(function* () {
112176
+ const make$38 = Effect.gen(function* () {
111210
112177
  const bitbucket = yield* BitbucketApi;
111211
112178
  /**
111212
112179
  * The repository's own path, and the workspace above it — which the people who may review are
@@ -111486,7 +112453,7 @@ const make$37 = Effect.gen(function* () {
111486
112453
  }).pipe(Effect.asVoid))
111487
112454
  });
111488
112455
  });
111489
- const layer$29 = Layer.effect(BitbucketPullRequestApi, make$37);
112456
+ const layer$30 = Layer.effect(BitbucketPullRequestApi, make$38);
111490
112457
  //#endregion
111491
112458
  //#region src/pullRequest/BitbucketPullRequestProvider.ts
111492
112459
  const CAPABILITIES$2 = {
@@ -111566,7 +112533,7 @@ function toChangeRequest(pullRequest) {
111566
112533
  labels: []
111567
112534
  };
111568
112535
  }
111569
- const make$36 = Effect.gen(function* () {
112536
+ const make$37 = Effect.gen(function* () {
111570
112537
  const api = yield* BitbucketPullRequestApi;
111571
112538
  const fail = (operation) => (error) => new PullRequestProviderError({
111572
112539
  provider: "bitbucket",
@@ -113795,7 +114762,7 @@ function actionArgs$1(action, mergeMethod, updateMethod) {
113795
114762
  case "reopen": return ["reopen"];
113796
114763
  }
113797
114764
  }
113798
- const make$35 = Effect.gen(function* () {
114765
+ const make$36 = Effect.gen(function* () {
113799
114766
  const github = yield* GitHubCli;
113800
114767
  /**
113801
114768
  * The pull request's own node id, which is what a mutation against the pull request itself is
@@ -114558,7 +115525,7 @@ const make$35 = Effect.gen(function* () {
114558
115525
  })))
114559
115526
  });
114560
115527
  });
114561
- const layer$28 = Layer.effect(GitHubPullRequestCli, make$35);
115528
+ const layer$29 = Layer.effect(GitHubPullRequestCli, make$36);
114562
115529
  //#endregion
114563
115530
  //#region src/pullRequest/GitHubPullRequestProvider.ts
114564
115531
  const CAPABILITIES$1 = {
@@ -114675,7 +115642,7 @@ function loginAvatarUrl(login, host) {
114675
115642
  }
114676
115643
  /** True where markdown would render nothing: whitespace, or only HTML comments. */
114677
115644
  const rendersEmpty = (body) => body.replace(/<!--[\s\S]*?-->/g, "").trim().length === 0;
114678
- const make$34 = Effect.gen(function* () {
115645
+ const make$35 = Effect.gen(function* () {
114679
115646
  const cli = yield* GitHubPullRequestCli;
114680
115647
  const fail = (operation) => (error) => new PullRequestProviderError({
114681
115648
  provider: "github",
@@ -115697,7 +116664,7 @@ function actionArgs(action, mergeMethod) {
115697
116664
  case "reopen": return ["reopen"];
115698
116665
  }
115699
116666
  }
115700
- const make$33 = Effect.gen(function* () {
116667
+ const make$34 = Effect.gen(function* () {
115701
116668
  const gitlab = yield* GitLabCli;
115702
116669
  const api = (input) => gitlab.execute({
115703
116670
  cwd: input.cwd,
@@ -116268,7 +117235,7 @@ const make$33 = Effect.gen(function* () {
116268
117235
  }).pipe(Effect.asVoid)
116269
117236
  });
116270
117237
  });
116271
- const layer$27 = Layer.effect(GitLabPullRequestCli, make$33);
117238
+ const layer$28 = Layer.effect(GitLabPullRequestCli, make$34);
116272
117239
  //#endregion
116273
117240
  //#region src/pullRequest/GitLabPullRequestProvider.ts
116274
117241
  const CAPABILITIES = {
@@ -116348,7 +117315,7 @@ function reasonFor(error) {
116348
117315
  if (error._tag === "GitLabCliAuthenticationError") return "unauthenticated";
116349
117316
  return "failed";
116350
117317
  }
116351
- const make$32 = Effect.gen(function* () {
117318
+ const make$33 = Effect.gen(function* () {
116352
117319
  const cli = yield* GitLabPullRequestCli;
116353
117320
  const fail = (operation) => (error) => new PullRequestProviderError({
116354
117321
  provider: "gitlab",
@@ -116491,13 +117458,13 @@ function fromProviders(providers) {
116491
117458
  * The hosts this build can read change requests from. A host with no entry here still shows up
116492
117459
  * in the provider list as unimplemented, so its projects are explained rather than missing.
116493
117460
  */
116494
- const make$31 = Effect.map(Effect.all([
116495
- make$34,
116496
- make$32,
116497
- make$36,
116498
- make$38
117461
+ const make$32 = Effect.map(Effect.all([
117462
+ make$35,
117463
+ make$33,
117464
+ make$37,
117465
+ make$39
116499
117466
  ]), fromProviders);
116500
- const layer$26 = Layer.effect(PullRequestProviderRegistry, make$31).pipe(Layer.provide(layer$28.pipe(Layer.provide(layer$37))), Layer.provide(layer$27.pipe(Layer.provide(layer$35))), Layer.provide(layer$29.pipe(Layer.provide(layer$39))), Layer.provide(layer$30.pipe(Layer.provide(layer$41))));
117467
+ const layer$27 = Layer.effect(PullRequestProviderRegistry, make$32).pipe(Layer.provide(layer$29.pipe(Layer.provide(layer$38))), Layer.provide(layer$28.pipe(Layer.provide(layer$36))), Layer.provide(layer$30.pipe(Layer.provide(layer$40))), Layer.provide(layer$31.pipe(Layer.provide(layer$42))));
116501
117468
  //#endregion
116502
117469
  //#region src/pullRequest/PullRequestService.ts
116503
117470
  /**
@@ -116685,7 +117652,7 @@ function repositoryIdentityOf(project) {
116685
117652
  if (identity.displayName) return identity.displayName;
116686
117653
  return identity.owner && identity.name ? `${identity.owner}/${identity.name}` : null;
116687
117654
  }
116688
- const make$30 = Effect.gen(function* () {
117655
+ const make$31 = Effect.gen(function* () {
116689
117656
  const registry = yield* PullRequestProviderRegistry;
116690
117657
  const projections = yield* ProjectionSnapshotQuery;
116691
117658
  const sourceControlProviders = yield* SourceControlProviderRegistry;
@@ -117683,7 +118650,7 @@ const make$30 = Effect.gen(function* () {
117683
118650
  invalidate
117684
118651
  });
117685
118652
  });
117686
- const layer$25 = Layer.effect(PullRequestService, make$30);
118653
+ const layer$26 = Layer.effect(PullRequestService, make$31);
117687
118654
  //#endregion
117688
118655
  //#region src/orchestration/ThreadWorkspaceLifecycle.ts
117689
118656
  var ThreadWorkspaceLifecycleError = class extends Data.TaggedError("ThreadWorkspaceLifecycleError") {};
@@ -117727,7 +118694,7 @@ const mapLifecycleError = Effect.mapError((cause) => cause instanceof ThreadWork
117727
118694
  detail: "Thread workspace lifecycle operation failed.",
117728
118695
  cause
117729
118696
  }));
117730
- const make$29 = Effect.gen(function* () {
118697
+ const make$30 = Effect.gen(function* () {
117731
118698
  const snapshots = yield* ProjectionSnapshotQuery;
117732
118699
  const engine = yield* OrchestrationEngineService;
117733
118700
  const gitWorkflow = yield* GitWorkflowService;
@@ -117981,7 +118948,7 @@ const make$29 = Effect.gen(function* () {
117981
118948
  record
117982
118949
  };
117983
118950
  });
117984
- const layer$24 = Layer.effect(ThreadWorkspaceLifecycleService, make$29);
118951
+ const layer$25 = Layer.effect(ThreadWorkspaceLifecycleService, make$30);
117985
118952
  //#endregion
117986
118953
  //#region src/textGeneration/BtwRequestCoordinator.ts
117987
118954
  const MAX_PENDING_BTW_CANCELLATIONS = 256;
@@ -120254,7 +121221,7 @@ function makeUpdateState(input) {
120254
121221
  output: input.output ?? null
120255
121222
  };
120256
121223
  }
120257
- const make$28 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
121224
+ const make$29 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
120258
121225
  const providerRegistry = yield* ProviderRegistry;
120259
121226
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
120260
121227
  const httpClient = yield* HttpClient.HttpClient;
@@ -120369,7 +121336,7 @@ const make$28 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
120369
121336
  });
120370
121337
  return ProviderMaintenanceRunner.of({ updateProvider });
120371
121338
  });
120372
- const layer$23 = Layer.effect(ProviderMaintenanceRunner, make$28());
121339
+ const layer$24 = Layer.effect(ProviderMaintenanceRunner, make$29());
120373
121340
  //#endregion
120374
121341
  //#region src/usage/usageTranscripts.ts
120375
121342
  const EMPTY_TOTALS = {
@@ -121176,29 +122143,33 @@ const ScanCacheJson = Schema$1.fromJsonString(Schema$1.Unknown);
121176
122143
  const decodeScanCacheFile = Schema$1.decodeUnknownEffect(ScanCacheJson);
121177
122144
  const encodeScanCacheFile = Schema$1.encodeEffect(ScanCacheJson);
121178
122145
  var UsageService = class extends Context.Service()("@p4code/cli/usage/UsageService") {};
121179
- Layer.succeed(UsageService, UsageService.of({ readSummary: (input) => Effect.succeed({
121180
- contractVersion: 4,
121181
- readAt: "1970-01-01T00:00:00.000Z",
121182
- timeZone: input.timeZone,
121183
- sinceDay: input.sinceDay,
121184
- untilDay: input.untilDay,
121185
- buckets: [],
121186
- sources: [],
121187
- pricing: {
121188
- status: "unavailable",
121189
- source: LITELLM_RATES_URL,
121190
- fetchedAt: null,
121191
- knownModels: 0
121192
- },
121193
- scanDurationMs: 0
121194
- }) }));
121195
- const make$27 = Effect.gen(function* () {
122146
+ Layer.succeed(UsageService, UsageService.of({
122147
+ readSessionRecords: () => Effect.succeed([]),
122148
+ readSummary: (input) => Effect.succeed({
122149
+ contractVersion: 4,
122150
+ readAt: "1970-01-01T00:00:00.000Z",
122151
+ timeZone: input.timeZone,
122152
+ sinceDay: input.sinceDay,
122153
+ untilDay: input.untilDay,
122154
+ buckets: [],
122155
+ sources: [],
122156
+ pricing: {
122157
+ status: "unavailable",
122158
+ source: LITELLM_RATES_URL,
122159
+ fetchedAt: null,
122160
+ knownModels: 0
122161
+ },
122162
+ scanDurationMs: 0
122163
+ })
122164
+ }));
122165
+ const make$28 = Effect.gen(function* () {
121196
122166
  const fileSystem = yield* FileSystem.FileSystem;
121197
122167
  const path = yield* Path$1.Path;
121198
122168
  const config = yield* ServerConfig$1;
121199
122169
  const settingsService = yield* ServerSettingsService;
121200
122170
  const httpClient = yield* HttpClient.HttpClient;
121201
122171
  const fileCache = /* @__PURE__ */ new Map();
122172
+ const scanLock = yield* Semaphore.make(1);
121202
122173
  let cacheDirty = false;
121203
122174
  const ratesCachePath = path.join(config.stateDir, "usage-model-rates.json");
121204
122175
  const scanCachePath = path.join(config.stateDir, "usage-scan-cache.json");
@@ -121309,7 +122280,7 @@ const make$27 = Effect.gen(function* () {
121309
122280
  cacheDirty = true;
121310
122281
  return tailRecords.length === 0 ? records : [...records, ...tailRecords];
121311
122282
  });
121312
- return { readSummary: Effect.fn("UsageService.readSummary")(function* (input) {
122283
+ const readSummary = Effect.fn("UsageService.readSummary")(function* (input) {
121313
122284
  if (input.sinceDay > input.untilDay) return yield* new UsageReadError({
121314
122285
  reason: "invalidWindow",
121315
122286
  detail: `sinceDay '${input.sinceDay}' is after untilDay '${input.untilDay}'`
@@ -121432,9 +122403,49 @@ const make$27 = Effect.gen(function* () {
121432
122403
  },
121433
122404
  scanDurationMs: Math.max(0, finishedAtMs - startedAtMs)
121434
122405
  };
121435
- }) };
122406
+ });
122407
+ const readSessionRecords = Effect.fn("UsageService.readSessionRecords")(function* (input) {
122408
+ if (input.sessionIds.length === 0) return [];
122409
+ yield* ensureScanCacheLoaded;
122410
+ const { dirs } = yield* resolveTranscriptDirs().pipe(Effect.provideService(Path$1.Path, path));
122411
+ const roots = dirs.flatMap((root) => root.provider === "codex" ? [root, {
122412
+ ...root,
122413
+ dir: path.join(path.dirname(root.dir), "archived_sessions")
122414
+ }] : [root]);
122415
+ const sessions = new Set(input.sessionIds);
122416
+ const seen = /* @__PURE__ */ new Set();
122417
+ const records = [];
122418
+ for (const { provider, dir } of roots) {
122419
+ const files = yield* Effect.promise(() => listTranscriptFiles(dir, input.sinceMs - MTIME_SLACK_MS));
122420
+ for (const file of files) {
122421
+ if (!input.sessionIds.some((id) => file.path.includes(id))) continue;
122422
+ const rows = yield* readFileRecords(file.path, file.size, file.mtimeMs, provider);
122423
+ for (const record of rows) {
122424
+ if (!sessions.has(record.sessionId) || record.timestampMs < input.sinceMs) continue;
122425
+ if (record.dedupeKey !== null) {
122426
+ if (seen.has(record.dedupeKey)) continue;
122427
+ seen.add(record.dedupeKey);
122428
+ }
122429
+ records.push(record);
122430
+ }
122431
+ }
122432
+ }
122433
+ const now = yield* Clock.currentTimeMillis;
122434
+ if (pruneScanCache(fileCache, {
122435
+ livePaths: /* @__PURE__ */ new Set(),
122436
+ walkedRoots: [],
122437
+ windowStartMs: input.sinceMs,
122438
+ retentionCutoffMs: now - CACHE_RETENTION_DAYS * 24 * 60 * 60 * 1e3
122439
+ }) > 0) cacheDirty = true;
122440
+ yield* persistScanCache();
122441
+ return records;
122442
+ });
122443
+ return {
122444
+ readSummary: (input) => scanLock.withPermits(1)(readSummary(input)),
122445
+ readSessionRecords: (input) => scanLock.withPermits(1)(readSessionRecords(input))
122446
+ };
121436
122447
  });
121437
- const layer$22 = Layer.effect(UsageService, make$27);
122448
+ const layer$23 = Layer.effect(UsageService, make$28);
121438
122449
  //#endregion
121439
122450
  //#region src/feed/FeedStore.ts
121440
122451
  const storageFailure = (message) => new FeedError({
@@ -121798,7 +122809,7 @@ const jsonRequest = Effect.fn("FeedService.jsonRequest")(function* (url, token,
121798
122809
  catch: () => fail("hub_unavailable", "Hub returned invalid JSON.")
121799
122810
  });
121800
122811
  });
121801
- const make$26 = Effect.gen(function* () {
122812
+ const make$27 = Effect.gen(function* () {
121802
122813
  const hubLink = yield* HubLink;
121803
122814
  const providers = yield* ProviderInstanceRegistry;
121804
122815
  const config = yield* ServerConfig$1;
@@ -122036,7 +123047,7 @@ var FeedService = class extends Context.Reference("@p4code/cli/feed/FeedService"
122036
123047
  markRead: unavailable,
122037
123048
  cleanup: unavailable
122038
123049
  }) }) {};
122039
- const layer$21 = Layer.effect(FeedService, make$26);
123050
+ const layer$22 = Layer.effect(FeedService, make$27);
122040
123051
  const SKILL_MANIFEST_FILENAME = "SKILL.md";
122041
123052
  /**
122042
123053
  * Split a catalogue id (`owner/repo/skill-name`) into its parts.
@@ -122182,7 +123193,7 @@ const emptyFetch = (id, unavailable) => ({
122182
123193
  skipped: [],
122183
123194
  unavailable
122184
123195
  });
122185
- const make$25 = Effect.gen(function* () {
123196
+ const make$26 = Effect.gen(function* () {
122186
123197
  const http = yield* HttpClient.HttpClient;
122187
123198
  const request = Effect.fn("SkillRegistry.request")(function* (url) {
122188
123199
  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));
@@ -122253,7 +123264,7 @@ const make$25 = Effect.gen(function* () {
122253
123264
  fetch
122254
123265
  };
122255
123266
  });
122256
- const layer$20 = Layer.effect(SkillRegistry, make$25);
123267
+ const layer$21 = Layer.effect(SkillRegistry, make$26);
122257
123268
  //#endregion
122258
123269
  //#region src/mcp/PreviewAutomationBroker.ts
122259
123270
  var PreviewAutomationBroker = class extends Context.Service()("@p4code/cli/mcp/PreviewAutomationBroker") {};
@@ -122371,7 +123382,7 @@ const classifyResponseError = (context, error) => {
122371
123382
  });
122372
123383
  }
122373
123384
  };
122374
- const make$24 = Effect.gen(function* PreviewAutomationBrokerMake() {
123385
+ const make$25 = Effect.gen(function* PreviewAutomationBrokerMake() {
122375
123386
  const crypto = yield* Crypto.Crypto;
122376
123387
  const state = yield* SynchronizedRef.make({
122377
123388
  clients: /* @__PURE__ */ new Map(),
@@ -122605,7 +123616,7 @@ const make$24 = Effect.gen(function* PreviewAutomationBrokerMake() {
122605
123616
  invoke
122606
123617
  });
122607
123618
  }).pipe(Effect.withSpan("PreviewAutomationBroker.make"));
122608
- const layer$19 = Layer.effect(PreviewAutomationBroker, make$24);
123619
+ const layer$20 = Layer.effect(PreviewAutomationBroker, make$25);
122609
123620
  //#endregion
122610
123621
  //#region src/preview/Manager.ts
122611
123622
  /**
@@ -122669,7 +123680,7 @@ const buildIdleSnapshot = (input) => ({
122669
123680
  viewport: FILL_PREVIEW_VIEWPORT,
122670
123681
  updatedAt: input.updatedAt
122671
123682
  });
122672
- const make$23 = Effect.gen(function* PreviewManagerMake() {
123683
+ const make$24 = Effect.gen(function* PreviewManagerMake() {
122673
123684
  const serverEpoch = NodeCrypto.randomUUID();
122674
123685
  const stateRef = yield* SynchronizedRef.make(initialState);
122675
123686
  const eventsPubSub = yield* PubSub.unbounded();
@@ -122900,7 +123911,7 @@ const make$23 = Effect.gen(function* PreviewManagerMake() {
122900
123911
  subscribeEvents: PubSub.subscribe(eventsPubSub)
122901
123912
  });
122902
123913
  }).pipe(Effect.withSpan("PreviewManager.make"));
122903
- const layer$18 = Layer.effect(PreviewManager, make$23);
123914
+ const layer$19 = Layer.effect(PreviewManager, make$24);
122904
123915
  //#endregion
122905
123916
  //#region src/workspace/WorkspaceSearchIndex.ts
122906
123917
  const WORKSPACE_INDEX_MAX_ENTRIES = 25e3;
@@ -123037,7 +124048,7 @@ const waitForScan = (cwd, finder, onFailure) => Effect.try({
123037
124048
  timeout: WORKSPACE_INDEX_SCAN_TIMEOUT
123038
124049
  })
123039
124050
  }), Effect.withSpan("WorkspaceSearchIndex.waitForScan"));
123040
- const make$22 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd, contentSearch = false) {
124051
+ const make$23 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd, contentSearch = false) {
123041
124052
  const finder = yield* Effect.acquireRelease(createFinder(cwd, contentSearch), (finder) => Effect.try({
123042
124053
  try: () => finder.destroy(),
123043
124054
  catch: (cause) => new WorkspaceSearchIndexDestroyFailed({
@@ -123177,14 +124188,14 @@ const make$22 = Effect.fn("WorkspaceSearchIndex.make")(function* (cwd, contentSe
123177
124188
  * workspace root. WorkspaceSearchIndexMap owns memoization and idle cleanup;
123178
124189
  * using a default cwd here would mix resources from different workspaces.
123179
124190
  */
123180
- const layer$17 = (cwd) => Layer.effect(WorkspaceSearchIndex, make$22(cwd));
124191
+ const layer$18 = (cwd) => Layer.effect(WorkspaceSearchIndex, make$23(cwd));
123181
124192
  var WorkspaceSearchIndexMap = class extends LayerMap.Service()("@p4code/cli/workspace/WorkspaceSearchIndexMap", {
123182
- lookup: layer$17,
124193
+ lookup: layer$18,
123183
124194
  idleTimeToLive: WORKSPACE_INDEX_IDLE_TTL
123184
124195
  }) {};
123185
124196
  /** Content indexing is allocated only when workspace content search is used. */
123186
124197
  var WorkspaceContentSearchIndexMap = class extends LayerMap.Service()("@p4code/cli/workspace/WorkspaceContentSearchIndexMap", {
123187
- lookup: (cwd) => Layer.effect(WorkspaceSearchIndex, make$22(cwd, true)),
124198
+ lookup: (cwd) => Layer.effect(WorkspaceSearchIndex, make$23(cwd, true)),
123188
124199
  idleTimeToLive: WORKSPACE_INDEX_IDLE_TTL
123189
124200
  }) {};
123190
124201
  //#endregion
@@ -123246,7 +124257,7 @@ const resolveBrowseTarget = Effect.fn("WorkspaceEntries.resolveBrowseTarget")(fu
123246
124257
  if (!input.cwd) return yield* new WorkspaceEntriesCurrentProjectRequiredError({ partialPath: input.partialPath });
123247
124258
  return path.resolve(expandHomePath$1(input.cwd, path), input.partialPath);
123248
124259
  });
123249
- const make$21 = Effect.gen(function* () {
124260
+ const make$22 = Effect.gen(function* () {
123250
124261
  const path = yield* Path$1.Path;
123251
124262
  const workspacePaths = yield* WorkspacePaths;
123252
124263
  const contentSearchIndexes = yield* WorkspaceContentSearchIndexMap;
@@ -123328,7 +124339,7 @@ const make$21 = Effect.gen(function* () {
123328
124339
  searchContents
123329
124340
  });
123330
124341
  });
123331
- const layer$16 = Layer.effect(WorkspaceEntries, make$21).pipe(Layer.provide(WorkspaceSearchIndexMap.layer), Layer.provide(WorkspaceContentSearchIndexMap.layer));
124342
+ const layer$17 = Layer.effect(WorkspaceEntries, make$22).pipe(Layer.provide(WorkspaceSearchIndexMap.layer), Layer.provide(WorkspaceContentSearchIndexMap.layer));
123332
124343
  //#endregion
123333
124344
  //#region src/workspace/WorkspaceFileSystem.ts
123334
124345
  /**
@@ -123387,7 +124398,7 @@ Schema$1.Union([
123387
124398
  ]);
123388
124399
  /** Service tag for workspace file operations. */
123389
124400
  var WorkspaceFileSystem = class extends Context.Service()("@p4code/cli/workspace/WorkspaceFileSystem") {};
123390
- const make$20 = Effect.gen(function* () {
124401
+ const make$21 = Effect.gen(function* () {
123391
124402
  const fileSystem = yield* FileSystem.FileSystem;
123392
124403
  const path = yield* Path$1.Path;
123393
124404
  const workspacePaths = yield* WorkspacePaths;
@@ -123531,7 +124542,7 @@ const make$20 = Effect.gen(function* () {
123531
124542
  writeFile
123532
124543
  });
123533
124544
  });
123534
- const layer$15 = Layer.effect(WorkspaceFileSystem, make$20);
124545
+ const layer$16 = Layer.effect(WorkspaceFileSystem, make$21);
123535
124546
  //#endregion
123536
124547
  //#region src/vcs/VcsStatusBroadcaster.ts
123537
124548
  const DEFAULT_VCS_STATUS_REFRESH_INTERVAL = Duration.seconds(30);
@@ -123613,7 +124624,7 @@ const autoPullPolicyLayer = Layer.effect(VcsAutoPullPolicy, Effect.gen(function*
123613
124624
  return resolveProjectAutoPull(yield* settingsService.getSettings, project.value.id);
123614
124625
  }, Effect.orElseSucceed(() => false)) };
123615
124626
  }));
123616
- const make$19 = Effect.gen(function* () {
124627
+ const make$20 = Effect.gen(function* () {
123617
124628
  const autoPullPolicy = yield* VcsAutoPullPolicy;
123618
124629
  const workflow = yield* GitWorkflowService;
123619
124630
  const fs = yield* FileSystem.FileSystem;
@@ -123872,7 +124883,7 @@ const make$19 = Effect.gen(function* () {
123872
124883
  streamStatus
123873
124884
  });
123874
124885
  });
123875
- const layer$14 = Layer.effect(VcsStatusBroadcaster, make$19);
124886
+ const layer$15 = Layer.effect(VcsStatusBroadcaster, make$20);
123876
124887
  //#endregion
123877
124888
  //#region src/vcs/VcsProvisioningService.ts
123878
124889
  var VcsProvisioningService = class extends Context.Service()("@p4code/cli/vcs/VcsProvisioningService") {};
@@ -123885,7 +124896,7 @@ function resolveRequestedKind(kind) {
123885
124896
  }));
123886
124897
  return Effect.succeed(kind);
123887
124898
  }
123888
- const make$18 = Effect.gen(function* () {
124899
+ const make$19 = Effect.gen(function* () {
123889
124900
  const registry = yield* VcsDriverRegistry;
123890
124901
  const initRepository = Effect.fn("VcsProvisioningService.initRepository")(function* (input) {
123891
124902
  const kind = yield* resolveRequestedKind(input.kind);
@@ -123893,11 +124904,11 @@ const make$18 = Effect.gen(function* () {
123893
124904
  });
123894
124905
  return VcsProvisioningService.of({ initRepository });
123895
124906
  });
123896
- const layer$13 = Layer.effect(VcsProvisioningService, make$18);
124907
+ const layer$14 = Layer.effect(VcsProvisioningService, make$19);
123897
124908
  //#endregion
123898
124909
  //#region src/review/ReviewService.ts
123899
124910
  var ReviewService = class extends Context.Service()("@p4code/cli/review/ReviewService") {};
123900
- const make$17 = Effect.gen(function* () {
124911
+ const make$18 = Effect.gen(function* () {
123901
124912
  const config = yield* ServerConfig$1;
123902
124913
  const fileSystem = yield* FileSystem.FileSystem;
123903
124914
  const path = yield* Path$1.Path;
@@ -123953,7 +124964,7 @@ const make$17 = Effect.gen(function* () {
123953
124964
  });
123954
124965
  return ReviewService.of({ getDiffPreview });
123955
124966
  });
123956
- const layer$12 = Layer.effect(ReviewService, make$17);
124967
+ const layer$13 = Layer.effect(ReviewService, make$18);
123957
124968
  //#endregion
123958
124969
  //#region src/diagnostics/ProcessDiagnostics.ts
123959
124970
  const PROCESS_QUERY_TIMEOUT_MS = 1e3;
@@ -124248,7 +125259,7 @@ function assertDescendantPid(pid) {
124248
125259
  }));
124249
125260
  }));
124250
125261
  }
124251
- const make$16 = Effect.gen(function* () {
125262
+ const make$17 = Effect.gen(function* () {
124252
125263
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
124253
125264
  const read = Effect.gen(function* () {
124254
125265
  const readAt = yield* DateTime.now;
@@ -124292,7 +125303,7 @@ const make$16 = Effect.gen(function* () {
124292
125303
  signal
124293
125304
  });
124294
125305
  });
124295
- const layer$11 = Layer.effect(ProcessDiagnostics, make$16);
125306
+ const layer$12 = Layer.effect(ProcessDiagnostics, make$17);
124296
125307
  //#endregion
124297
125308
  //#region src/diagnostics/ProcessResourceMonitor.ts
124298
125309
  const SAMPLE_INTERVAL_MS = 5e3;
@@ -124443,7 +125454,7 @@ function aggregateProcessResourceHistory(input) {
124443
125454
  }) : Option.none()
124444
125455
  };
124445
125456
  }
124446
- const make$15 = Effect.gen(function* () {
125457
+ const make$16 = Effect.gen(function* () {
124447
125458
  const spawner = yield* ChildProcessSpawner$1.ChildProcessSpawner;
124448
125459
  const state = yield* Ref.make({
124449
125460
  samples: [],
@@ -124492,7 +125503,7 @@ const make$15 = Effect.gen(function* () {
124492
125503
  });
124493
125504
  return ProcessResourceMonitor.of({ readHistory });
124494
125505
  });
124495
- const layer$10 = Layer.effect(ProcessResourceMonitor, make$15);
125506
+ const layer$11 = Layer.effect(ProcessResourceMonitor, make$16);
124496
125507
  //#endregion
124497
125508
  //#region src/diagnostics/TraceDiagnostics.ts
124498
125509
  var TraceFileReadError = class extends Schema$1.TaggedErrorClass()("TraceFileReadError", {
@@ -124740,7 +125751,7 @@ function readTraceFile(fileSystem, path) {
124740
125751
  cause
124741
125752
  })) }));
124742
125753
  }
124743
- const make$14 = Effect.gen(function* () {
125754
+ const make$15 = Effect.gen(function* () {
124744
125755
  const fileSystem = yield* FileSystem.FileSystem;
124745
125756
  const read = Effect.fn("TraceDiagnostics.read")(function* (options) {
124746
125757
  const readAt = options.readAt ?? (yield* DateTime.now);
@@ -124784,7 +125795,7 @@ const make$14 = Effect.gen(function* () {
124784
125795
  });
124785
125796
  return TraceDiagnostics.of({ read });
124786
125797
  });
124787
- const layer$9 = Layer.effect(TraceDiagnostics, make$14);
125798
+ const layer$10 = Layer.effect(TraceDiagnostics, make$15);
124788
125799
  function readTraceDiagnostics(options) {
124789
125800
  return Effect.gen(function* () {
124790
125801
  return yield* (yield* TraceDiagnostics).read(options);
@@ -124819,7 +125830,7 @@ function snapshotFromDetail(link, detail, syncedAt) {
124819
125830
  mergeability: detail.mergeability
124820
125831
  };
124821
125832
  }
124822
- const make$13 = Effect.gen(function* () {
125833
+ const make$14 = Effect.gen(function* () {
124823
125834
  const engine = yield* OrchestrationEngineService;
124824
125835
  const projections = yield* ProjectionSnapshotQuery;
124825
125836
  const pullRequests = yield* PullRequestService;
@@ -124913,7 +125924,7 @@ const make$13 = Effect.gen(function* () {
124913
125924
  };
124914
125925
  });
124915
125926
  var PullRequestSyncReactor = class extends Context.Service()("@p4code/cli/orchestration/Layers/PullRequestSyncReactor") {};
124916
- const PullRequestSyncReactorLive = Layer.effect(PullRequestSyncReactor, make$13);
125927
+ const PullRequestSyncReactorLive = Layer.effect(PullRequestSyncReactor, make$14);
124917
125928
  //#endregion
124918
125929
  //#region src/sourceControl/SourceControlDiscovery.ts
124919
125930
  const VCS_PROBES = [{
@@ -124932,7 +125943,7 @@ const VCS_PROBES = [{
124932
125943
  installHint: "Install Jujutsu with `brew install jj` or from https://github.com/jj-vcs/jj."
124933
125944
  }];
124934
125945
  var SourceControlDiscovery = class extends Context.Service()("@p4code/cli/sourceControl/SourceControlDiscovery") {};
124935
- const make$12 = Effect.gen(function* () {
125946
+ const make$13 = Effect.gen(function* () {
124936
125947
  const config = yield* ServerConfig$1;
124937
125948
  const process = yield* VcsProcess;
124938
125949
  const sourceControlProviders = yield* SourceControlProviderRegistry;
@@ -124981,7 +125992,7 @@ const make$12 = Effect.gen(function* () {
124981
125992
  sourceControlProviders: sourceControlProviders.discover
124982
125993
  }) });
124983
125994
  });
124984
- const layer$8 = Layer.effect(SourceControlDiscovery, make$12);
125995
+ const layer$9 = Layer.effect(SourceControlDiscovery, make$13);
124985
125996
  //#endregion
124986
125997
  //#region src/sourceControl/SourceControlRepositoryService.ts
124987
125998
  const isSourceControlRepositoryError = Schema$1.is(SourceControlRepositoryError);
@@ -125014,7 +126025,7 @@ function expandHomePath(input, path) {
125014
126025
  if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
125015
126026
  return input;
125016
126027
  }
125017
- const make$11 = Effect.gen(function* () {
126028
+ const make$12 = Effect.gen(function* () {
125018
126029
  const config = yield* ServerConfig$1;
125019
126030
  const fileSystem = yield* FileSystem.FileSystem;
125020
126031
  const git = yield* GitVcsDriver;
@@ -125153,7 +126164,7 @@ const make$11 = Effect.gen(function* () {
125153
126164
  publishRepository: (input) => publishRepository(input).pipe(mapRepositoryError("publishRepository", input.provider))
125154
126165
  });
125155
126166
  });
125156
- const layer$7 = Layer.effect(SourceControlRepositoryService, make$11);
126167
+ const layer$8 = Layer.effect(SourceControlRepositoryService, make$12);
125157
126168
  //#endregion
125158
126169
  //#region src/ws.ts
125159
126170
  /** Matches `p4c hub token add`, so a token minted here and one minted there are the same thing. */
@@ -125300,6 +126311,7 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
125300
126311
  [WS_METHODS.serverUpdateServer, AuthOrchestrationOperateScope],
125301
126312
  [WS_METHODS.serverUpsertKeybinding, AuthOrchestrationOperateScope],
125302
126313
  [WS_METHODS.serverRemoveKeybinding, AuthOrchestrationOperateScope],
126314
+ [WS_METHODS.threadHandoff, AuthOrchestrationReadScope],
125303
126315
  [WS_METHODS.btwAsk, AuthOrchestrationReadScope],
125304
126316
  [WS_METHODS.btwCancel, AuthOrchestrationReadScope],
125305
126317
  [WS_METHODS.tasksList, AuthOrchestrationReadScope],
@@ -126152,6 +127164,27 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
126152
127164
  operation: "getUsageReport",
126153
127165
  detail: "This provider does not report account usage."
126154
127166
  })), { "rpc.aggregate": "server" }),
127167
+ [WS_METHODS.threadHandoff]: (input) => observeRpcEffect$1(WS_METHODS.threadHandoff, Effect.gen(function* () {
127168
+ const fail = (detail) => new TextGenerationError({
127169
+ operation: "createHandoff",
127170
+ detail
127171
+ });
127172
+ if (!projectionSnapshotQuery.getHandoffContext || !textGeneration.generateBtwAnswer) return yield* fail("Handoff generation is unavailable on this server.");
127173
+ const context = yield* projectionSnapshotQuery.getHandoffContext(input.threadId).pipe(Effect.mapError(() => fail("Could not read thread history.")));
127174
+ if (Option.isNone(context)) return yield* fail("Thread or project is no longer available.");
127175
+ const settings = yield* serverSettings.getSettings.pipe(Effect.mapError(() => fail("Could not read text generation settings.")));
127176
+ const result = yield* textGeneration.generateBtwAnswer({
127177
+ cwd: context.value.projectId === P4_CHAT_PROJECT_ID ? config.chatWorkspaceDir : context.value.cwd,
127178
+ modelSelection: settings.textGenerationModelSelection,
127179
+ context: context.value.transcript,
127180
+ question: "Write a self-contained handoff for a new coding-agent thread continuing this work. Include objective, latest user constraints, decisions, completed changes, verification evidence and its limits, pending work, important paths and identifiers. Reconcile builder and watcher histories without duplicating their shared facts. Later user corrections win. Preserve explicit authorization limits; do not invent approvals or claim missing work is complete. Histories may be bounded: say when earlier context is unavailable. Return only the handoff in concise Markdown, addressed to the next agent."
127181
+ });
127182
+ if (!result.answer.trim()) return yield* fail("Model returned an empty handoff. Try again.");
127183
+ return {
127184
+ projectId: context.value.projectId,
127185
+ text: result.answer.trim()
127186
+ };
127187
+ }), { "rpc.aggregate": "thread" }),
126155
127188
  [WS_METHODS.btwAsk]: (input) => observeRpcEffect$1(WS_METHODS.btwAsk, btw ? btw.ask(input) : Effect.fail(new BtwAskError({ detail: "Contextual questions are unavailable on this server." })), { "rpc.aggregate": "server" }),
126156
127189
  [WS_METHODS.btwCancel]: (input) => observeRpcEffect$1(WS_METHODS.btwCancel, btw ? btw.cancel(input) : Effect.succeed({}), { "rpc.aggregate": "server" }),
126157
127190
  [WS_METHODS.serverGetUsageSummary]: (input) => observeRpcEffect$1(WS_METHODS.serverGetUsageSummary, usage.readSummary(input), { "rpc.aggregate": "server" }),
@@ -126593,7 +127626,7 @@ const websocketRpcRouteLayer = Layer.unwrap(Effect.gen(function* () {
126593
127626
  const serverAuth = yield* EnvironmentAuth;
126594
127627
  const sessions = yield* SessionStore;
126595
127628
  const session = yield* serverAuth.authenticateWebSocketUpgrade(request).pipe(Effect.catchIf(isServerAuthCredentialError, (error) => failEnvironmentAuthInvalid(serverAuthCredentialReason(error))), Effect.catchIf(isServerAuthInternalError, (error) => failEnvironmentInternal("internal_error", error)));
126596
- const rpcWebSocketHttpEffect = yield* RpcServer.toHttpEffectWebsocket(WsRpcGroup, { disableTracing: true }).pipe(Effect.provide(makeWsRpcLayer(session, previewAutomationBroker, computerUseBroker).pipe(Layer.provideMerge(RpcSerialization.layerJson), Layer.provide(layer$23), Layer.provide(Layer.succeed(ServerSelfUpdate, serverSelfUpdate)), Layer.provide(Layer.succeed(PullRequestService, pullRequests)), Layer.provide(Layer.succeed(TaskRepositoryRegistry, taskRepositories)), Layer.provide(Layer.succeed(ProjectionProjectRepository, projectionProjects)), Layer.provide(Layer.succeed(P4ProjectFileLoader, p4ProjectFileLoader)), Layer.provide(layer$8.pipe(Layer.provide(layer$33.pipe(Layer.provide(Layer.mergeAll(layer$41, layer$39, layer$37, layer$35)), Layer.provideMerge(layer$49), Layer.provide(layer$47.pipe(Layer.provide(layer$48))))), Layer.provide(layer$50))))));
127629
+ const rpcWebSocketHttpEffect = yield* RpcServer.toHttpEffectWebsocket(WsRpcGroup, { disableTracing: true }).pipe(Effect.provide(makeWsRpcLayer(session, previewAutomationBroker, computerUseBroker).pipe(Layer.provideMerge(RpcSerialization.layerJson), Layer.provide(layer$24), Layer.provide(Layer.succeed(ServerSelfUpdate, serverSelfUpdate)), Layer.provide(Layer.succeed(PullRequestService, pullRequests)), Layer.provide(Layer.succeed(TaskRepositoryRegistry, taskRepositories)), Layer.provide(Layer.succeed(ProjectionProjectRepository, projectionProjects)), Layer.provide(Layer.succeed(P4ProjectFileLoader, p4ProjectFileLoader)), Layer.provide(layer$9.pipe(Layer.provide(layer$34.pipe(Layer.provide(Layer.mergeAll(layer$42, layer$40, layer$38, layer$36)), Layer.provideMerge(layer$50), Layer.provide(layer$48.pipe(Layer.provide(layer$49))))), Layer.provide(layer$51))))));
126597
127630
  return yield* Effect.acquireUseRelease(sessions.markConnected(session.sessionId), () => rpcWebSocketHttpEffect, () => sessions.markDisconnected(session.sessionId));
126598
127631
  }).pipe(Effect.catchTags({
126599
127632
  EnvironmentAuthInvalidError: HttpServerRespondable.toResponse,
@@ -126658,7 +127691,7 @@ function toPersistenceSqlOrDecodeError(sqlOperation, decodeOperation, correlatio
126658
127691
  cause
126659
127692
  });
126660
127693
  }
126661
- const make$10 = Effect.gen(function* () {
127694
+ const make$11 = Effect.gen(function* () {
126662
127695
  const sql = yield* SqlClient.SqlClient;
126663
127696
  const upsertRuntimeRow = SqlSchema.void({
126664
127697
  Request: ProviderSessionRuntimeDbRowSchema,
@@ -126761,7 +127794,7 @@ const make$10 = Effect.gen(function* () {
126761
127794
  deleteByThreadId
126762
127795
  };
126763
127796
  });
126764
- const layer$6 = Layer.effect(ProviderSessionRuntimeRepository, make$10);
127797
+ const layer$7 = Layer.effect(ProviderSessionRuntimeRepository, make$11);
126765
127798
  //#endregion
126766
127799
  //#region src/provider/Layers/ProviderSessionDirectory.ts
126767
127800
  const decodeProviderDriverKindValue = Schema$1.decodeUnknownEffect(ProviderDriverKind);
@@ -126912,6 +127945,13 @@ const makeProviderAdapterRegistry = Effect.fn("makeProviderAdapterRegistry")(fun
126912
127945
  });
126913
127946
  const ProviderAdapterRegistryLive = Layer.effect(ProviderAdapterRegistry, makeProviderAdapterRegistry());
126914
127947
  //#endregion
127948
+ //#region src/provider/ComputerUsePrompts.ts
127949
+ /** Routing guidance is injected only when the session has P4's Computer Use endpoint. */
127950
+ const COMPUTER_USE_ROUTING_PROMPT = "P4 Computer Use is enabled for this session through the cua_repl MCP server. For natural-language requests to inspect or interact with a native desktop app, use its js tool by default, even when the user does not mention P4 or Computer Use. Do not substitute provider-builtin Computer Use, AppleScript, shell automation, or another integration unless the user explicitly requests that path. For a named app, start directly with const app = await cua.getApp(exactNameOrBundleId); read the automatically returned state/image and stop if it answers the request. Reuse the handle and batch safe actions; do not repeat selection or unchanged state. For Device Hub, simulator, emulator, or connected-device requests, use cua.listDevices() once, then cua.getDevice({hostId,deviceId,platform}) with exact discovered IDs. Selection already emits initial device state. Reuse its handle; do not print the handle or repeat that state before acting. Never substitute macOS getApp('Simulator'). Device hosts are scoped to this provider environment and can differ from the desktop host. If device discovery, opening, inspection, or interaction reports missing permission or unavailable control, stop and report the exact limitation. Do not retry through screenshots, shell simctl, Xcode tools, or another MCP server. Do not call getState/getApp, control the P4 desktop app, or open device settings to work around a device API failure. Missing consent, unavailable devices, or unsupported operations are limitations to report, not permission to switch hosts or bypass the tool.";
127951
+ function withComputerUseRouting(input, enabled) {
127952
+ return enabled ? `<p4-computer-use>\n${COMPUTER_USE_ROUTING_PROMPT}\n</p4-computer-use>\n\n${input ?? ""}` : input;
127953
+ }
127954
+ //#endregion
126915
127955
  //#region src/mcp/McpSessionRegistry.ts
126916
127956
  var McpSessionRegistry = class extends Context.Service()("@p4code/cli/mcp/McpSessionRegistry") {};
126917
127957
  /**
@@ -127214,7 +128254,7 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
127214
128254
  });
127215
128255
  });
127216
128256
  let activeMcpSessionRegistry;
127217
- const make$9 = Effect.acquireRelease(Effect.gen(function* () {
128257
+ const make$10 = Effect.acquireRelease(Effect.gen(function* () {
127218
128258
  const broker = yield* Effect.serviceOption(ComputerUseBroker);
127219
128259
  return yield* makeWithOptions(Option.isSome(broker) ? { onRevoke: broker.value.invalidate } : {});
127220
128260
  }).pipe(Effect.tap((registry) => Effect.sync(() => {
@@ -127222,7 +128262,7 @@ const make$9 = Effect.acquireRelease(Effect.gen(function* () {
127222
128262
  }))), (registry) => registry.revokeAll.pipe(Effect.andThen(Effect.sync(() => {
127223
128263
  if (activeMcpSessionRegistry === registry) activeMcpSessionRegistry = void 0;
127224
128264
  }))));
127225
- const layer$5 = Layer.effect(McpSessionRegistry, make$9);
128265
+ const layer$6 = Layer.effect(McpSessionRegistry, make$10);
127226
128266
  const issueActiveMcpCredential = (request) => activeMcpSessionRegistry ? activeMcpSessionRegistry.revokeThread(request.threadId).pipe(Effect.andThen(activeMcpSessionRegistry.issue(request))) : Effect.sync(() => void 0);
127227
128267
  /**
127228
128268
  * Refreshes the liveness of a thread's MCP credential. Called on every provider
@@ -127685,7 +128725,10 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
127685
128725
  ...input.modelSelection?.model ? { "provider.model": input.modelSelection.model } : {}
127686
128726
  });
127687
128727
  yield* touchActiveMcpThread(input.threadId);
127688
- const turn = yield* routed.adapter.sendTurn(input);
128728
+ const turn = yield* routed.adapter.sendTurn({
128729
+ ...input,
128730
+ input: withComputerUseRouting(input.input, !forCompaction && Boolean(readMcpProviderSession(input.threadId)?.computerUse))
128731
+ });
127689
128732
  yield* directory.upsert({
127690
128733
  threadId: input.threadId,
127691
128734
  provider: routed.adapter.provider,
@@ -128170,7 +129213,7 @@ const mcpToolkit = (toolkit) => Layer.effectDiscard(Effect.gen(function* () {
128170
129213
  //#endregion
128171
129214
  //#region src/mcp/toolkits/computerUse/tools.ts
128172
129215
  const ComputerUseJsTool = Tool.make("js", {
128173
- description: "Run JavaScript in a persistent P4 Computer Use REPL. Start with await cua.getState(). Use cua.createBrowserTab('iab', url, {visible:true}) or cua.getTab(tabId) for P4 browser tabs. Tab methods: getState(), goto(url), click({locator}), type({text,locator?,clear?}), press({key,modifiers?}), scroll({deltaY,deltaX?}). Print results with nodeRepl.write(value); emit PNG/JPEG with nodeRepl.emitImage({mimeType,data}) using base64 data. Top-level await and persistent let bindings are supported. Await each operation. Reset after timeout or host disconnect. For available native apps, use cua.getApp(exactNameOrBundleId), then getAXState(), click(elementIndex), or setValue(elementIndex,text) to replace a field value in the background. Selection and getAXState print numbered accessibility state; Several deterministic actions against distinct unchanged elements may share one snapshot. Each used handle expires; the host revalidates remaining native handles and rejects changed elements. Read fresh state after the action batch before deciding the next action. getScreenshot() emits the selected app window; getAXStateAndScreenshot() emits both. Legacy getState(), click({elementId}), type({elementId,text}), windows(), and screenshot(windowId) remain supported. Native control preserves foreground focus and the system pointer. Coordinate, key, drag, scroll, and activation input are unsupported; do not use global input or activate the app as a fallback. P4Code shows the controlled native window and a persistent independent white pointer with a blue glow in its embedded live view. Hide closes only the view; use Show live view in the Computer Use control to reopen it. Stop ends thread control; reset clears the view and handles. Google Chrome can be controlled as a native app using cua.getApp('Google Chrome'); Chrome extension/CDP tab APIs are not exposed by this host. Native app availability and macOS permission states are reported by the host; never assume native apps are supported. The host may be a different machine from the provider server. Device hub simulators are scoped to the provider environment: await cua.listDevices(), then await cua.getDevice({hostId,deviceId,platform}) using exact discovered IDs. The device handle supports getAXState() (raw iOS accessibility state), getScreenshot(), getAXStateAndScreenshot(), click([x,y]) in device coordinates from fresh AX state, typeText(text), and close(). No device key, scroll, or drag APIs are currently exposed. Device consent must already be enabled in P4 Settings. Use timeout_ms:60000 for initial device startup. Reset invalidates device handles; device viewer sessions remain under Devices until explicitly closed. Never substitute macOS Simulator.app AX for simulated-app state.",
129216
+ description: "P4 Code Computer Use: control native apps through P4Code's signed helper. Run JavaScript in a persistent REPL. On the first call, execute exactly one entry-point operation and read its automatic output before continuing. When the user names an app, execute only const app = await cua.getApp(exactNameBundleIdOrPath); this already prints its state and includes a screenshot when AX exposes no app content. Use await cua.getState() only when discovery is needed. Use cua.createBrowserTab('iab', url, {visible:true}) or cua.getTab(tabId) for P4 browser tabs. Tab methods: getState(), goto(url), click({locator}), type({text,locator?,clear?}), press({key,modifiers?}), scroll({deltaY,deltaX?}). Print results with nodeRepl.write(value); emit PNG/JPEG with nodeRepl.emitImage({mimeType,data}) using base64 data. Top-level await and persistent let bindings are supported. Await each operation. Bindings disappear after helper restart, host disconnect, reset, or tray Stop. When resuming a task, guard a remembered binding within the same js call: if (typeof app === 'undefined') { globalThis.app = await cua.getApp('Spotify'); } else { await app.getScreenshot(); }. Substitute the named app and intended operation. The missing-binding branch selects only and prints fresh state; decide the next action after reading it. Never spend a separate tool call checking whether a binding exists. Reset after timeout or host disconnect. For available native apps, use cua.getApp(exactNameBundleIdOrPath), then getAXState(), click(elementIndex), or setValue(elementIndex,text) to replace a field value in the background. Selection already prints numbered accessibility state; reuse the app handle without selecting or reading it again before the first action. Snapshot text is bounded; use getAXState({filter:'Pause'}) for case-insensitive text filtering across the snapshot, including controls omitted from the initial output. Use {emit:false} on getApp(name, options), getAXState(options), or getScreenshot(options) when processing the returned value yourself; do not print auto-emitted output again. Several deterministic actions against distinct unchanged elements may share one snapshot. Each used element index expires after one action. To click the same control again, refresh with getAXState({filter: controlLabel}) first, even inside a batch; never reuse its old index. The host revalidates remaining native handles and rejects changed elements. Read fresh state after the action batch before deciding the next action. If selection includes a screenshot because AX exposes only window controls or menus, read that image immediately and finish if it answers the request. If no image is included, use getScreenshot() to read visual content; do not repeat unchanged snapshots hoping for missing accessibility content. getScreenshot() emits the selected app window; getAXStateAndScreenshot() emits both. Legacy getState(), click({elementId}), type({elementId,text}), windows(), and screenshot(windowId) remain supported. Native control preserves foreground focus and the system pointer. Coordinate, key, drag, scroll, and activation input are unsupported; do not use global input or activate the app as a fallback. P4Code shows the controlled native window and a persistent independent black pointer with a thin contrast edge in its embedded live view. Hide closes only the view; use Show live view in the Computer Use control to reopen it. Stop ends thread control; reset clears the view and handles. Google Chrome can be controlled as a native app using cua.getApp('Google Chrome'); Chrome extension/CDP tab APIs are not exposed by this host. Native app availability and macOS permission states are reported by the host; never assume native apps are supported. The host may be a different machine from the provider server. Device hub simulators are scoped to the provider environment: await cua.listDevices(), then await cua.getDevice({hostId,deviceId,platform}) using exact discovered IDs. Selection automatically emits initial AX state (or a screenshot on other platforms); reuse the handle without repeating that capture or printing the handle. A permission or unavailable-control error requires stopping and reporting it, never shell or alternative-tool fallback. The device handle supports getAXState() (raw iOS accessibility state), getScreenshot(), getAXStateAndScreenshot(), click([x,y]) in device coordinates from fresh AX state, typeText(text), and close(). When getAXState() reports adapter apple-xcode or xcodebuildmcp, use semantic refs from capture.elements and only advertised capabilities: click(elementRef), typeText(text,{elementRef,replaceExisting:true}), swipe({withinElementRef,direction}), drag({elementRef,direction}), batch([{action:\"tap\",elementRef},...]), and recordVideo(\"start\"|\"stop\") only when advertised in capabilities. getAXState({sinceScreenHash}) avoids unchanged snapshots. Batch only same-screen taps; action output includes updated capture. On stale-ref errors inspect fresh state and choose a current ref; never replay an action automatically. Physical-device UI control and device key input are unavailable. Device consent must already be enabled in P4 Settings. Use timeout_ms:60000 for initial device startup. Reset invalidates device handles; device viewer sessions remain under Devices until explicitly closed. Never substitute macOS Simulator.app AX for simulated-app state.",
128174
129217
  parameters: Schema$1.Struct({
128175
129218
  ...ComputerUseJsInput.fields,
128176
129219
  hostId: Schema$1.optional(TrimmedNonEmptyString).annotate({ description: "Select an available host when more than one is connected. Reset before changing hosts." })
@@ -128629,7 +129672,7 @@ const stringField = (record, key) => {
128629
129672
  const value = record[key];
128630
129673
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
128631
129674
  };
128632
- const make$8 = Effect.gen(function* () {
129675
+ const make$9 = Effect.gen(function* () {
128633
129676
  const linear = yield* LinearClient;
128634
129677
  return { resolve: Effect.fn("TicketResolver.resolve")(function* (reference) {
128635
129678
  const identifier = parseTicketReference(reference);
@@ -128660,7 +129703,7 @@ const make$8 = Effect.gen(function* () {
128660
129703
  };
128661
129704
  }) };
128662
129705
  });
128663
- const layer$4 = Layer.effect(TicketResolver, make$8);
129706
+ const layer$5 = Layer.effect(TicketResolver, make$9);
128664
129707
  //#endregion
128665
129708
  //#region src/mcp/toolkits/tasks/tools.ts
128666
129709
  const dependencies = [McpInvocationContext, TaskRepository];
@@ -130001,6 +131044,34 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
130001
131044
  })
130002
131045
  });
130003
131046
  //#endregion
131047
+ //#region src/orchestration/FusionBudgetPause.ts
131048
+ const pauseFusionBudget = Effect.fn("pauseFusionBudget")(function* (input) {
131049
+ const engine = yield* OrchestrationEngineService;
131050
+ const epoch = input.pair.reviewExperiment?.lastMessageSequence ?? 0;
131051
+ for (const threadId of [input.pair.implementerThreadId, input.pair.watcherThreadId]) {
131052
+ const id = `fusion-budget:${input.pair.id}:${epoch}:${threadId}`;
131053
+ yield* engine.dispatch({
131054
+ type: "thread.activity.append",
131055
+ commandId: CommandId.make(id),
131056
+ threadId,
131057
+ activity: {
131058
+ id: EventId.make(id),
131059
+ tone: "info",
131060
+ kind: "fusion.pair.paused",
131061
+ summary: `Fusion paused: ${input.reason}`,
131062
+ payload: {
131063
+ pairId: input.pair.id,
131064
+ reason: "review-budget",
131065
+ experiment: input.pair.reviewExperiment
131066
+ },
131067
+ turnId: null,
131068
+ createdAt: input.createdAt
131069
+ },
131070
+ createdAt: input.createdAt
131071
+ });
131072
+ }
131073
+ });
131074
+ //#endregion
130004
131075
  //#region src/mcp/toolkits/advise/tools.ts
130005
131076
  /**
130006
131077
  * The write half of cross-thread access, and the reason advise is its own
@@ -130104,6 +131175,22 @@ const AdviseToolkitHandlersLive = AdviseToolkit.toLayer({
130104
131175
  };
130105
131176
  }
130106
131177
  const createdAt = DateTime.formatIso(yield* DateTime.now);
131178
+ const activePair = readModel.threadPairs?.find((pair) => pair.detachedAt === null && pair.implementerThreadId === input.threadId);
131179
+ const budgetBlock = activePair && fusionBudgetBlock(activePair, input.threadId, `[fusion-advice] ${input.message}`);
131180
+ if (budgetBlock && activePair) {
131181
+ yield* pauseFusionBudget({
131182
+ pair: activePair,
131183
+ reason: budgetBlock,
131184
+ createdAt
131185
+ }).pipe(Effect.mapError((cause) => new ThreadAdviseFailedError({
131186
+ threadId: input.threadId,
131187
+ detail: cause.message
131188
+ })));
131189
+ return yield* new ThreadAdviseFailedError({
131190
+ threadId: input.threadId,
131191
+ detail: budgetBlock
131192
+ });
131193
+ }
130107
131194
  const interrupted = input.interrupt === true;
130108
131195
  if (interrupted) yield* dispatchAdvise({
130109
131196
  type: "thread.turn.interrupt",
@@ -130391,7 +131478,7 @@ const ComputerUseMcpLive = ComputerUseToolkitRegistrationLive.pipe(Layer.provide
130391
131478
  version: version$1,
130392
131479
  path: "/mcp/computer-use"
130393
131480
  }).pipe(Layer.provide(McpAuthMiddlewareLive))));
130394
- const layer$3 = Layer.mergeAll(Layer.fresh(StandardMcpLive), Layer.fresh(ComputerUseMcpLive));
131481
+ const layer$4 = Layer.mergeAll(Layer.fresh(StandardMcpLive), Layer.fresh(ComputerUseMcpLive));
130395
131482
  //#endregion
130396
131483
  //#region src/hub/HubActivityTransport.ts
130397
131484
  const REQUEST_TIMEOUT_MS = 8e3;
@@ -130681,7 +131768,7 @@ const runHubActivityEmission = Effect.fn("HubActivityRuntime.run")(function* (se
130681
131768
  const outbound = yield* makeHubActivityOutbound(identity.destination);
130682
131769
  yield* runHubActivityTick(outbound, transport, identity).pipe(Effect.tap(() => ready), Effect.repeat(Schedule.spaced(Duration.millis(HUB_ACTIVITY_INTERVAL_MS))));
130683
131770
  });
130684
- const layer$2 = Layer.effectDiscard(Effect.gen(function* () {
131771
+ const layer$3 = Layer.effectDiscard(Effect.gen(function* () {
130685
131772
  yield* runHubTunnelReconnect((yield* HubLink).changes, (settings, ready) => runHubActivityEmission(settings, ready).pipe(Effect.mapError((error) => error instanceof HubActivityUnauthorized ? new HubUnauthorizedError() : error))).pipe(Effect.forkScoped);
130686
131773
  }));
130687
131774
  //#endregion
@@ -131506,7 +132593,7 @@ const runHubTunnelOutbound = Effect.fn("HubTunnelOutbound.run")(function* (setti
131506
132593
  yield* runHubTunnelPump(lease, socket, makeHubTunnelEnvironmentDispatch(localOrigin));
131507
132594
  }));
131508
132595
  });
131509
- const layer$1 = Layer.effectDiscard(Effect.gen(function* () {
132596
+ const layer$2 = Layer.effectDiscard(Effect.gen(function* () {
131510
132597
  const link = yield* HubLink;
131511
132598
  const server = yield* HttpServer.HttpServer;
131512
132599
  if (server.address._tag !== "TcpAddress") return;
@@ -131578,7 +132665,7 @@ const COMPLETION_SWEEP_BATCH_SIZE = 25;
131578
132665
  const COMPLETION_SWEEP_INTERVAL = Duration.minutes(1);
131579
132666
  const COMPLETION_PR_TIMEOUT = Duration.seconds(10);
131580
132667
  const MILLISECONDS_PER_DAY = 1440 * 60 * 1e3;
131581
- const make$7 = Effect.gen(function* () {
132668
+ const make$8 = Effect.gen(function* () {
131582
132669
  const sql = yield* SqlClient.SqlClient;
131583
132670
  const settings = yield* ServerSettingsService;
131584
132671
  const git = yield* GitManager;
@@ -131665,7 +132752,7 @@ const make$7 = Effect.gen(function* () {
131665
132752
  };
131666
132753
  });
131667
132754
  var ThreadCompletionReactor = class extends Context.Service()("@p4code/cli/orchestration/Layers/ThreadCompletionReactor") {};
131668
- const ThreadCompletionReactorLive = Layer.effect(ThreadCompletionReactor, make$7);
132755
+ const ThreadCompletionReactorLive = Layer.effect(ThreadCompletionReactor, make$8);
131669
132756
  //#endregion
131670
132757
  //#region src/orchestration/Services/ScheduledTaskReactor.ts
131671
132758
  /**
@@ -131702,6 +132789,238 @@ const makeOrchestrationReactor = Effect.gen(function* () {
131702
132789
  });
131703
132790
  const OrchestrationReactorLive = Layer.effect(OrchestrationReactor, makeOrchestrationReactor);
131704
132791
  //#endregion
132792
+ //#region src/usage/fusionObservation.ts
132793
+ const OBSERVATION_DAY_MS = 1440 * 60 * 1e3;
132794
+ const REVIEW_RATIO_THRESHOLD = .5;
132795
+ const REVIEW_COUNT_THRESHOLD = 10;
132796
+ const MILLISECONDS_PER_MINUTE = 60 * 1e3;
132797
+ function emptyRole() {
132798
+ return {
132799
+ turns: 0,
132800
+ failedTurns: 0,
132801
+ turnsWithUsage: 0,
132802
+ activeMinutes: 0,
132803
+ tokens: { ...EMPTY_TOTALS }
132804
+ };
132805
+ }
132806
+ function observationWeek(date) {
132807
+ const instant = DateTime.makeUnsafe(date);
132808
+ const daysSinceMonday = (DateTime.getPartUtc(instant, "weekDay") + 7 - 1) % 7;
132809
+ return DateTime.formatIso(DateTime.makeUnsafe(DateTime.toEpochMillis(instant) - daysSinceMonday * OBSERVATION_DAY_MS)).slice(0, 10);
132810
+ }
132811
+ /** Role consumption is descriptive; it cannot estimate a counterfactual solo run. */
132812
+ function buildFusionObservation(input) {
132813
+ const sinceMs = Date.parse(input.since);
132814
+ const untilMs = Date.parse(input.generatedAt);
132815
+ const pairs = /* @__PURE__ */ new Map();
132816
+ const sessions = /* @__PURE__ */ new Map();
132817
+ let missingSessionTurns = 0;
132818
+ for (const turn of input.turns) {
132819
+ const start = Math.max(sinceMs, Date.parse(turn.startedAt), Date.parse(turn.pairCreatedAt));
132820
+ const end = Math.min(untilMs, Date.parse(turn.completedAt), turn.detachedAt === null ? untilMs : Date.parse(turn.detachedAt));
132821
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) continue;
132822
+ let pair = pairs.get(turn.pairId);
132823
+ if (!pair) {
132824
+ pair = {
132825
+ pairId: turn.pairId,
132826
+ reviewPolicy: turn.reviewPolicy ?? null,
132827
+ builder: emptyRole(),
132828
+ review: emptyRole(),
132829
+ supervisorOther: emptyRole()
132830
+ };
132831
+ pairs.set(turn.pairId, pair);
132832
+ }
132833
+ const role = turn.role === "builder" ? pair.builder : turn.trigger === "review" || turn.trigger === "gate" ? pair.review : pair.supervisorOther;
132834
+ role.turns += 1;
132835
+ role.failedTurns += Number(turn.state !== "completed");
132836
+ role.activeMinutes += (end - start) / MILLISECONDS_PER_MINUTE;
132837
+ if (turn.sessionId === null) {
132838
+ missingSessionTurns += 1;
132839
+ continue;
132840
+ }
132841
+ const entries = sessions.get(turn.sessionId) ?? [];
132842
+ entries.push({
132843
+ start,
132844
+ end,
132845
+ role,
132846
+ observed: false
132847
+ });
132848
+ sessions.set(turn.sessionId, entries);
132849
+ }
132850
+ let unattributedRecords = 0;
132851
+ let ambiguousRecords = 0;
132852
+ const seen = /* @__PURE__ */ new Set();
132853
+ for (const record of input.records) {
132854
+ if (record.timestampMs < sinceMs || record.timestampMs > untilMs) continue;
132855
+ if (record.dedupeKey !== null) {
132856
+ if (seen.has(record.dedupeKey)) continue;
132857
+ seen.add(record.dedupeKey);
132858
+ }
132859
+ const matching = (sessions.get(record.sessionId) ?? []).filter((turn) => record.timestampMs >= turn.start && record.timestampMs <= turn.end);
132860
+ if (matching.length !== 1) {
132861
+ unattributedRecords += 1;
132862
+ if (matching.length > 1) ambiguousRecords += 1;
132863
+ continue;
132864
+ }
132865
+ const match = matching[0];
132866
+ match.role.tokens = addTotals(match.role.tokens, record.totals);
132867
+ if (!match.observed) match.role.turnsWithUsage += 1;
132868
+ match.observed = true;
132869
+ }
132870
+ const observedPairs = [...pairs.values()].map((pair) => {
132871
+ const builderTokens = totalTokens(pair.builder.tokens);
132872
+ const reviewTokens = totalTokens(pair.review.tokens);
132873
+ return {
132874
+ ...pair,
132875
+ reviewTokensPerBuilderToken: builderTokens > 0 && reviewTokens > 0 ? reviewTokens / builderTokens : null
132876
+ };
132877
+ });
132878
+ const ratios = observedPairs.flatMap((pair) => pair.reviewTokensPerBuilderToken === null ? [] : [pair.reviewTokensPerBuilderToken]).sort((a, b) => a - b);
132879
+ const middle = Math.floor(ratios.length / 2);
132880
+ const median = ratios.length === 0 ? null : ratios.length % 2 === 0 ? (ratios[middle - 1] + ratios[middle]) / 2 : ratios[middle];
132881
+ const recommendations = [];
132882
+ const hasIncompleteUsage = missingSessionTurns > 0 || observedPairs.some((pair) => [
132883
+ pair.builder,
132884
+ pair.review,
132885
+ pair.supervisorOther
132886
+ ].some((role) => role.turnsWithUsage < role.turns));
132887
+ if (hasIncompleteUsage) recommendations.push("Improve usage attribution before comparing policies; missing usage is not zero consumption.");
132888
+ if (ratios.length < 20) recommendations.push(`Collect at least 20 pairs with builder and review usage before selecting a policy experiment.`);
132889
+ else if (!hasIncompleteUsage && median !== null && median > REVIEW_RATIO_THRESHOLD) recommendations.push("Trial fewer review boundaries against the current policy on fixed tasks; compare independently verified outcomes and total cost.");
132890
+ if (observedPairs.some((pair) => pair.review.turns > REVIEW_COUNT_THRESHOLD)) recommendations.push(`Inspect pairs with more than ${REVIEW_COUNT_THRESHOLD} review turns for overly small phases or repeated corrections; a high count alone does not establish waste.`);
132891
+ recommendations.push("Keep settings unchanged until an isolated comparison passes acceptance and regression checks. These observations do not measure defects prevented or solo performance.");
132892
+ return {
132893
+ version: 1,
132894
+ mode: "observe",
132895
+ generatedAt: input.generatedAt,
132896
+ since: input.since,
132897
+ timeZone: "UTC",
132898
+ coverage: {
132899
+ pairs: observedPairs.length,
132900
+ pairsWithBothUsage: ratios.length,
132901
+ missingSessionTurns,
132902
+ unattributedRecords,
132903
+ ambiguousRecords
132904
+ },
132905
+ medianReviewTokensPerBuilderToken: median,
132906
+ limitations: [
132907
+ "Only completed or failed turn intervals are counted; ongoing turns are excluded.",
132908
+ "Current session linkage may omit usage before a provider session reset; unsupported providers remain unmeasured.",
132909
+ "Codex subagent sessions are not linked; Claude descendants sharing the parent session id can be included.",
132910
+ "Cached tokens are included. Tokens and wall time are not billing, quota, latency overhead, or causal quality measurements.",
132911
+ "Supervisor startup and direct user turns are separate from automatic reviews; review-induced builder repairs are not separable."
132912
+ ],
132913
+ pairs: observedPairs,
132914
+ recommendations
132915
+ };
132916
+ }
132917
+ //#endregion
132918
+ //#region src/usage/FusionObservationService.ts
132919
+ const OBSERVATION_CHECK_INTERVAL = Duration.hours(1);
132920
+ const ReportStamp = Schema$1.Struct({
132921
+ version: Schema$1.Literal(1),
132922
+ generatedAt: Schema$1.String
132923
+ });
132924
+ const TurnRow = Schema$1.Struct({
132925
+ pairId: Schema$1.String,
132926
+ reviewPolicy: Schema$1.NullOr(Schema$1.fromJsonString(FusionReviewPolicy)),
132927
+ threadId: Schema$1.String,
132928
+ turnId: Schema$1.String,
132929
+ role: Schema$1.Literals(["builder", "supervisor"]),
132930
+ sessionId: Schema$1.NullOr(Schema$1.String),
132931
+ pairCreatedAt: Schema$1.String,
132932
+ detachedAt: Schema$1.NullOr(Schema$1.String),
132933
+ startedAt: Schema$1.String,
132934
+ completedAt: Schema$1.String,
132935
+ state: Schema$1.String,
132936
+ trigger: Schema$1.Literals([
132937
+ "review",
132938
+ "gate",
132939
+ "advice",
132940
+ "other"
132941
+ ])
132942
+ });
132943
+ const decodeReportStamp = Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(ReportStamp));
132944
+ const decodeTurns = Schema$1.decodeUnknownEffect(Schema$1.Array(TurnRow));
132945
+ const encodeReport = Schema$1.encodeEffect(Schema$1.UnknownFromJsonString);
132946
+ /** Reads existing projections only; observation never dispatches orchestration commands. */
132947
+ const make$7 = Effect.gen(function* () {
132948
+ const sql = yield* SqlClient.SqlClient;
132949
+ const fs = yield* FileSystem.FileSystem;
132950
+ const path = yield* Path$1.Path;
132951
+ const config = yield* ServerConfig$1;
132952
+ const settings = yield* ServerSettingsService;
132953
+ const usage = yield* UsageService;
132954
+ const lock = yield* Semaphore.make(1);
132955
+ const directory = path.join(config.stateDir, "fusion-observation");
132956
+ const latestPath = path.join(directory, "latest.json");
132957
+ const writeReport = Effect.fn("FusionObservation.writeReport")(function* (destination, body) {
132958
+ const temporary = `${destination}.tmp`;
132959
+ yield* fs.writeFileString(temporary, body);
132960
+ yield* fs.rename(temporary, destination);
132961
+ });
132962
+ const refresh = Effect.fn("FusionObservation.refresh")(function* () {
132963
+ if (!(yield* settings.getSettings).enableFusionObservation) return;
132964
+ const now = yield* Clock.currentTimeMillis;
132965
+ const generatedAt = DateTime.formatIso(DateTime.makeUnsafe(now));
132966
+ const weeklyPath = path.join(directory, `week-${observationWeek(generatedAt)}.json`);
132967
+ const stamp = yield* fs.readFileString(latestPath).pipe(Effect.flatMap(decodeReportStamp), Effect.orElseSucceed(() => null));
132968
+ const weeklyExists = yield* fs.exists(weeklyPath);
132969
+ if (stamp?.generatedAt.slice(0, 10) === generatedAt.slice(0, 10) && weeklyExists) return;
132970
+ const since = DateTime.formatIso(DateTime.makeUnsafe(now - 7 * OBSERVATION_DAY_MS));
132971
+ const rows = yield* sql`
132972
+ SELECT p.pair_id AS "pairId", t.thread_id AS "threadId", t.turn_id AS "turnId",
132973
+ CASE WHEN t.thread_id = p.implementer_thread_id THEN 'builder' ELSE 'supervisor' END AS role,
132974
+ CASE WHEN json_valid(r.resume_cursor_json) THEN
132975
+ CASE WHEN r.provider_name = 'codex' THEN json_extract(r.resume_cursor_json, '$.threadId')
132976
+ WHEN r.provider_name = 'claudeAgent' THEN json_extract(r.resume_cursor_json, '$.resume') END
132977
+ END AS "sessionId",
132978
+ json_extract(p.review_experiment_json, '$.policy') AS "reviewPolicy",
132979
+ p.created_at AS "pairCreatedAt", p.detached_at AS "detachedAt",
132980
+ coalesce(t.started_at, t.requested_at) AS "startedAt", t.completed_at AS "completedAt", t.state,
132981
+ CASE WHEN substr(m.text, 1, 15) = '[fusion-review]' THEN 'review'
132982
+ WHEN substr(m.text, 1, 13) = '[fusion-gate]' THEN 'gate'
132983
+ WHEN substr(m.text, 1, 15) = '[fusion-advice]' THEN 'advice' ELSE 'other' END AS trigger
132984
+ FROM thread_pairs p
132985
+ JOIN projection_turns t ON t.thread_id IN (p.implementer_thread_id, p.watcher_thread_id)
132986
+ LEFT JOIN provider_session_runtime r ON r.thread_id = t.thread_id
132987
+ LEFT JOIN projection_thread_messages m ON m.message_id = t.pending_message_id
132988
+ WHERE t.turn_id IS NOT NULL AND t.completed_at >= ${since} AND t.completed_at <= ${generatedAt}
132989
+ AND t.completed_at >= p.created_at
132990
+ AND (p.detached_at IS NULL OR t.requested_at <= p.detached_at)
132991
+ `;
132992
+ const turns = yield* decodeTurns(rows);
132993
+ const sessionIds = [...new Set(turns.flatMap((turn) => turn.sessionId === null ? [] : [turn.sessionId]))];
132994
+ const report = buildFusionObservation({
132995
+ generatedAt,
132996
+ since,
132997
+ turns,
132998
+ records: yield* usage.readSessionRecords({
132999
+ sessionIds,
133000
+ sinceMs: Date.parse(since)
133001
+ })
133002
+ });
133003
+ yield* fs.makeDirectory(directory, { recursive: true });
133004
+ const body = yield* encodeReport(report);
133005
+ if (!weeklyExists) {
133006
+ yield* writeReport(weeklyPath, body);
133007
+ yield* Effect.logInfo("Weekly Fusion observation available", {
133008
+ path: weeklyPath,
133009
+ pairs: report.coverage.pairs
133010
+ });
133011
+ }
133012
+ yield* writeReport(latestPath, body);
133013
+ });
133014
+ const run = lock.withPermits(1)(refresh());
133015
+ return {
133016
+ refresh: run,
133017
+ start: run.pipe(Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.failCause(cause) : Effect.logWarning("Fusion observation failed; retrying on the next scheduled check")), Effect.repeat(Schedule.spaced(OBSERVATION_CHECK_INTERVAL)), Effect.forkScoped)
133018
+ };
133019
+ });
133020
+ const layer$1 = Layer.effectDiscard(Effect.gen(function* () {
133021
+ yield* (yield* make$7).start;
133022
+ }));
133023
+ //#endregion
131705
133024
  //#region src/orchestration/Services/RuntimeReceiptBus.ts
131706
133025
  /**
131707
133026
  * RuntimeReceiptBus - Internal checkpoint-reactor synchronization receipts.
@@ -132158,6 +133477,7 @@ function runtimeEventToActivities(event, taskTitle, compressMode) {
132158
133477
  summary: event.payload.description.trim().length > 0 ? truncateDetail(event.payload.description, 120) : "Reasoning update",
132159
133478
  payload: {
132160
133479
  taskId: event.payload.taskId,
133480
+ ...event.payload.model ? { model: event.payload.model } : {},
132161
133481
  ...event.provider === "claudeAgent" && event.payload.workflowScript?.instanceId === event.providerInstanceId ? { workflowScript: event.payload.workflowScript } : {},
132162
133482
  ...event.payload.description.trim().length > 0 ? { title: truncateDetail(event.payload.description, 120) } : {},
132163
133483
  detail: event.payload.summary ?? event.payload.description,
@@ -132919,7 +134239,7 @@ const make$6 = Effect.gen(function* () {
132919
134239
  drain: worker.drain
132920
134240
  };
132921
134241
  });
132922
- const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$6).pipe(Layer.provide(ProjectionTurnRepositoryLive), Layer.provide(layer$82));
134242
+ const ProviderRuntimeIngestionLive = Layer.effect(ProviderRuntimeIngestionService, make$6).pipe(Layer.provide(ProjectionTurnRepositoryLive), Layer.provide(layer$83));
132923
134243
  //#endregion
132924
134244
  //#region src/provider/userInvokedSkills.ts
132925
134245
  /**
@@ -133759,6 +135079,7 @@ const make$5 = Effect.gen(function* () {
133759
135079
  ].join("\n\n") : fusionRole === "watcher" && isFusionWatcherWakeMessageId(input.messageId) ? sidechatSeededInput : [
133760
135080
  sessionFusionRole === fusionRole ? fusionRoleReferenceLineFor(fusionRole) : fusionRoleInstructionsFor(fusionRole),
133761
135081
  fusionPairContext(activeFusionPair, fusionRole),
135082
+ fusionReviewPolicyInstructions(activeFusionPair.reviewExperiment),
133762
135083
  sidechatSeededInput
133763
135084
  ].join("\n\n");
133764
135085
  const providerHasStructuredQuestionSystemPrompt = activeSession?.provider === "claudeAgent" || activeSession?.provider === "codex";
@@ -134841,7 +136162,7 @@ Then thread_gate_respond, threadId ${input.implementerThreadId}, gateId ${input.
134841
136162
  - "approve": clear gate, continue builder.
134842
136163
  - "object" plus message: send objection, spend round. After ${input.roundCap} objections, escalate to user.
134843
136164
 
134844
- No answer within ${Math.round(input.gateTimeoutMs / 1e3)} seconds: fail open, record unwatched. Answer, briefly explain to user, end turn; never wait for builder.`;
136165
+ No answer within ${Math.round(input.gateTimeoutMs / 1e3)} seconds: ${input.failClosed ? "escalate to the user; keep work unapproved" : "fail open, record unwatched"}. Answer, briefly explain to user, end turn; never wait for builder.`;
134845
136166
  const make$3 = Effect.gen(function* () {
134846
136167
  const orchestrationEngine = yield* OrchestrationEngineService;
134847
136168
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
@@ -134944,6 +136265,9 @@ const make$3 = Effect.gen(function* () {
134944
136265
  discard: true
134945
136266
  });
134946
136267
  });
136268
+ const wakeWasRequested = Effect.fn("FusionWatcherReactor.wakeWasRequested")(function* (afterSequence, throughSequence, messageId) {
136269
+ return (yield* orchestrationEngine.readEvents(afterSequence, throughSequence - afterSequence, { eventTypes: ["thread.turn-start-requested"] }).pipe(Stream.takeWhile((event) => event.sequence <= throughSequence), Stream.filter((event) => event.type === "thread.turn-start-requested" && event.payload.messageId === messageId), Stream.take(1), Stream.runCollect)).length > 0;
136270
+ });
134947
136271
  /** Starts one watcher turn for the open gate and advances the pair cursor. */
134948
136272
  const wakeWatcherForGate = Effect.fn("FusionWatcherReactor.wakeWatcherForGate")(function* (event) {
134949
136273
  const gate = event.payload.gate;
@@ -134951,8 +136275,26 @@ const make$3 = Effect.gen(function* () {
134951
136275
  const { readModel, activePairs } = yield* readPairs;
134952
136276
  const pair = activePairs.find((candidate) => candidate.id === event.payload.pairId);
134953
136277
  if (pair === void 0) return;
136278
+ if (pair.reviewExperiment && (pair.activeGate?.id !== gate.id || pair.activeGate.state !== "awaiting-watcher" || pair.activeGate.round !== gate.round)) return;
134954
136279
  const watcher = readModel.threads.find((thread) => thread.id === pair.watcherThreadId && thread.deletedAt === null);
134955
136280
  if (watcher === void 0) return;
136281
+ const budgetBlock = fusionBudgetBlock(pair, watcher.id, FUSION_GATE_PROMPT_PREFIX);
136282
+ if (budgetBlock && !(yield* wakeWasRequested(event.sequence, readModel.snapshotSequence, gateMessageId(gate.id, gate.round)))) {
136283
+ yield* orchestrationEngine.dispatch({
136284
+ type: "thread-pair.gate.advance",
136285
+ commandId: gateCommandId(pair.id, gate.id, `budget:${gate.round}`),
136286
+ pairId: pair.id,
136287
+ gateId: gate.id,
136288
+ transition: "escalate",
136289
+ advancedAt: gate.updatedAt
136290
+ });
136291
+ yield* pauseFusionBudget({
136292
+ pair,
136293
+ reason: budgetBlock,
136294
+ createdAt: gate.updatedAt
136295
+ });
136296
+ return;
136297
+ }
134956
136298
  yield* orchestrationEngine.dispatch({
134957
136299
  type: "thread.turn.start",
134958
136300
  commandId: gateCommandId(pair.id, gate.id, `wake:${gate.round}`),
@@ -134965,9 +136307,10 @@ const make$3 = Effect.gen(function* () {
134965
136307
  gate,
134966
136308
  roundCap: pair.roundCap,
134967
136309
  gateTimeoutMs: pair.gateTimeoutMs,
136310
+ failClosed: pair.reviewExperiment != null,
134968
136311
  afterSequence: pair.lastReviewedImplementerSequence,
134969
136312
  throughSequence: event.sequence
134970
- }),
136313
+ }) + "\n\n" + fusionReviewPolicyInstructions(pair.reviewExperiment),
134971
136314
  attachments: []
134972
136315
  },
134973
136316
  runtimeMode: watcher.runtimeMode,
@@ -135301,6 +136644,15 @@ const make$3 = Effect.gen(function* () {
135301
136644
  implementerSequence: completion.sequence,
135302
136645
  advancedAt: completion.occurredAt
135303
136646
  });
136647
+ const budgetBlock = fusionBudgetBlock(currentPair, watcher.id, FUSION_REVIEW_PROMPT_PREFIX);
136648
+ if (budgetBlock && !(yield* wakeWasRequested(completion.sequence, readModel.snapshotSequence, reviewMessageId(currentPair.id, completion.sequence)))) {
136649
+ yield* pauseFusionBudget({
136650
+ pair: currentPair,
136651
+ reason: budgetBlock,
136652
+ createdAt: completion.occurredAt
136653
+ });
136654
+ return;
136655
+ }
135304
136656
  const idleCycles = turn.startedByAdvice && !turn.humanMessage && !turn.ranTools ? (idleAdviceCycles.get(currentPair.id) ?? 0) + 1 : 0;
135305
136657
  idleAdviceCycles.set(currentPair.id, idleCycles);
135306
136658
  if (idleCycles >= 3) {
@@ -135324,7 +136676,7 @@ const make$3 = Effect.gen(function* () {
135324
136676
  implementerThreadId: currentPair.implementerThreadId,
135325
136677
  afterSequence: currentPair.lastReviewedImplementerSequence,
135326
136678
  throughSequence: completion.sequence
135327
- }),
136679
+ }) + "\n\n" + fusionReviewPolicyInstructions(currentPair.reviewExperiment),
135328
136680
  attachments: []
135329
136681
  },
135330
136682
  runtimeMode: watcher.runtimeMode,
@@ -135378,6 +136730,7 @@ const make$3 = Effect.gen(function* () {
135378
136730
  });
135379
136731
  completions = completions.filter((completion) => completion !== activationCompletion);
135380
136732
  }
136733
+ if (pair.reviewExperiment) completions = completions.filter((completion) => completion.payload.state === "completed").slice(-1);
135381
136734
  let boundary = activationCompletion?.sequence ?? pair.lastReviewedImplementerSequence;
135382
136735
  for (const completion of completions.filter((candidate) => candidate.payload.state === "completed")) {
135383
136736
  const turn = describeBuilderTurn({
@@ -135425,6 +136778,17 @@ const make$3 = Effect.gen(function* () {
135425
136778
  if (gate.awaitingWatcherSince === null) continue;
135426
136779
  const since = Date.parse(gate.awaitingWatcherSince);
135427
136780
  if (Number.isNaN(since) || nowMillis - since < pair.gateTimeoutMs) continue;
136781
+ if (pair.reviewExperiment) {
136782
+ yield* orchestrationEngine.dispatch({
136783
+ type: "thread-pair.gate.advance",
136784
+ commandId: gateCommandId(pair.id, gate.id, `timeout-escalate:${gate.round}`),
136785
+ pairId: pair.id,
136786
+ gateId: gate.id,
136787
+ transition: "escalate",
136788
+ advancedAt: DateTime.formatIso(now)
136789
+ });
136790
+ continue;
136791
+ }
135428
136792
  yield* orchestrationEngine.dispatch({
135429
136793
  type: "thread-pair.gate.resolve",
135430
136794
  commandId: gateCommandId(pair.id, gate.id, `timeout:${gate.round}`),
@@ -136441,7 +137805,7 @@ const ObservabilityLive = Layer.unwrap(Effect.gen(function* () {
136441
137805
  sink,
136442
137806
  ...delegate ? { delegate } : {}
136443
137807
  });
136444
- return Layer.mergeAll(Layer.succeed(Tracer.Tracer, tracer), layer$61(sink));
137808
+ return Layer.mergeAll(Layer.succeed(Tracer.Tracer, tracer), layer$62(sink));
136445
137809
  })).pipe(Layer.provide(OtlpExporter.layerFlusher), Layer.provideMerge(otlpSerializationLayer));
136446
137810
  const metricsLayer = config.otlpMetricsUrl === void 0 ? Layer.empty : OtlpMetrics.layer({
136447
137811
  url: config.otlpMetricsUrl,
@@ -136813,31 +138177,31 @@ const PlatformServicesLive = Layer.unwrap(Effect.gen(function* () {
136813
138177
  }
136814
138178
  }));
136815
138179
  const ReactorLayerLive = Layer.empty.pipe(Layer.provideMerge(OrchestrationReactorLive), Layer.provideMerge(ProviderRuntimeIngestionLive), Layer.provideMerge(ProviderCommandReactorLive), Layer.provideMerge(CheckpointReactorLive), Layer.provideMerge(ThreadDeletionReactorLive), Layer.provideMerge(FusionWatcherReactorLive), Layer.provideMerge(ScheduledTaskReactorLive), Layer.provideMerge(ThreadCompletionReactorLive), Layer.provideMerge(PullRequestSyncReactorLive), Layer.provideMerge(RuntimeReceiptBusLive));
136816
- const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(Layer.provide(layer$6));
138180
+ const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(Layer.provide(layer$7));
136817
138181
  const ProviderLayerLive = ProviderServiceLive.pipe(Layer.provide(ProviderAdapterRegistryLive), Layer.provideMerge(ProviderSessionDirectoryLayerLive));
136818
138182
  const PersistenceLayerLive = Layer.empty.pipe(Layer.provideMerge(layerConfig));
136819
- const VcsDriverRegistryLayerLive = layer$47.pipe(Layer.provide(layer$48));
136820
- const SourceControlProviderRegistryLayerLive = layer$33.pipe(Layer.provide(Layer.mergeAll(layer$41, layer$39, layer$37, layer$35)), Layer.provideMerge(layer$49), Layer.provideMerge(VcsDriverRegistryLayerLive));
136821
- const GitManagerLayerLive = layer$32.pipe(Layer.provideMerge(layer$42), Layer.provideMerge(layer$49), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(layer$58));
136822
- const GitLayerLive = Layer.empty.pipe(Layer.provideMerge(GitManagerLayerLive), Layer.provideMerge(layer$49));
136823
- const GitWorkflowLayerLive = layer$31.pipe(Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(GitLayerLive));
136824
- const SourceControlRepositoryServiceLayerLive = layer$7.pipe(Layer.provideMerge(layer$49), Layer.provideMerge(SourceControlProviderRegistryLayerLive));
136825
- const ReviewLayerLive = layer$12.pipe(Layer.provideMerge(layer$49), Layer.provideMerge(VcsDriverRegistryLayerLive));
136826
- const VcsLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$48), Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(layer$13.pipe(Layer.provide(VcsDriverRegistryLayerLive))), Layer.provideMerge(GitWorkflowLayerLive), Layer.provideMerge(ReviewLayerLive), Layer.provideMerge(SourceControlRepositoryServiceLayerLive), Layer.provideMerge(layer$14.pipe(Layer.provide(autoPullPolicyLayer), Layer.provide(GitWorkflowLayerLive))));
136827
- const PullRequestServiceLive = layer$25.pipe(Layer.provide(layer$26), Layer.provide(SourceControlProviderRegistryLayerLive), Layer.provide(layer$50));
136828
- const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$45), Layer.provideMerge(layer$46.pipe(Layer.provide(VcsDriverRegistryLayerLive))));
136829
- const PortScannerLayerLive = layer$44.pipe(Layer.provide(layer$81));
136830
- const TerminalLayerLive = layer$43.pipe(Layer.provide(PtyAdapterLive), Layer.provide(PortScannerLayerLive));
136831
- const PreviewLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$18), Layer.provideMerge(PortScannerLayerLive));
136832
- const WorkspaceEntriesLayerLive = layer$16.pipe(Layer.provide(layer$68));
136833
- const WorkspaceFileSystemLayerLive = layer$15.pipe(Layer.provide(layer$68), Layer.provide(WorkspaceEntriesLayerLive));
138183
+ const VcsDriverRegistryLayerLive = layer$48.pipe(Layer.provide(layer$49));
138184
+ const SourceControlProviderRegistryLayerLive = layer$34.pipe(Layer.provide(Layer.mergeAll(layer$42, layer$40, layer$38, layer$36)), Layer.provideMerge(layer$50), Layer.provideMerge(VcsDriverRegistryLayerLive));
138185
+ const GitManagerLayerLive = layer$33.pipe(Layer.provideMerge(layer$43), Layer.provideMerge(layer$50), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(layer$59));
138186
+ const GitLayerLive = Layer.empty.pipe(Layer.provideMerge(GitManagerLayerLive), Layer.provideMerge(layer$50));
138187
+ const GitWorkflowLayerLive = layer$32.pipe(Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(GitLayerLive));
138188
+ const SourceControlRepositoryServiceLayerLive = layer$8.pipe(Layer.provideMerge(layer$50), Layer.provideMerge(SourceControlProviderRegistryLayerLive));
138189
+ const ReviewLayerLive = layer$13.pipe(Layer.provideMerge(layer$50), Layer.provideMerge(VcsDriverRegistryLayerLive));
138190
+ const VcsLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$49), Layer.provideMerge(VcsDriverRegistryLayerLive), Layer.provideMerge(layer$14.pipe(Layer.provide(VcsDriverRegistryLayerLive))), Layer.provideMerge(GitWorkflowLayerLive), Layer.provideMerge(ReviewLayerLive), Layer.provideMerge(SourceControlRepositoryServiceLayerLive), Layer.provideMerge(layer$15.pipe(Layer.provide(autoPullPolicyLayer), Layer.provide(GitWorkflowLayerLive))));
138191
+ const PullRequestServiceLive = layer$26.pipe(Layer.provide(layer$27), Layer.provide(SourceControlProviderRegistryLayerLive), Layer.provide(layer$51));
138192
+ const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$46), Layer.provideMerge(layer$47.pipe(Layer.provide(VcsDriverRegistryLayerLive))));
138193
+ const PortScannerLayerLive = layer$45.pipe(Layer.provide(layer$82));
138194
+ const TerminalLayerLive = layer$44.pipe(Layer.provide(PtyAdapterLive), Layer.provide(PortScannerLayerLive));
138195
+ const PreviewLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$19), Layer.provideMerge(PortScannerLayerLive));
138196
+ const WorkspaceEntriesLayerLive = layer$17.pipe(Layer.provide(layer$68));
138197
+ const WorkspaceFileSystemLayerLive = layer$16.pipe(Layer.provide(layer$68), Layer.provide(WorkspaceEntriesLayerLive));
136834
138198
  const WorkspaceLayerLive = Layer.mergeAll(layer$68, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
136835
- const ProjectFaviconResolverLayerLive = layer$62.pipe(Layer.provide(layer$68), Layer.provide(layer$63));
136836
- const AuthLayerLive = layer$110.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$114));
138199
+ const ProjectFaviconResolverLayerLive = layer$63.pipe(Layer.provide(layer$68), Layer.provide(layer$64));
138200
+ const AuthLayerLive = layer$111.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$115));
136837
138201
  const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
136838
- const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(layer$24), 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$85)), Layer.provideMerge(layer$79), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(layer$57), Layer.provideMerge(Layer.mergeAll(layer$56, layer$70.pipe(Layer.provide(layer$81)), layer$66, layer$67)), Layer.provideMerge(layer$51), Layer.provideMerge(layer$52), Layer.provideMerge(layer$53), Layer.provideMerge(layer$54), Layer.provideMerge(layer$55)).pipe(Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(AntigravityInstallation.layer), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$59), Layer.provideMerge(layer$89.pipe(Layer.provide(layer$114))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$80), Layer.provideMerge(layer$65), Layer.provideMerge(remoteComputerUseBindingsLayer.pipe(Layer.provide(layer$85))), Layer.provideMerge(layer$73), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$4.pipe(Layer.provideMerge(LinearClientLive), Layer.provideMerge(Layer.mergeAll(LinearMcpClientLive, TrackerSecretsLive)), Layer.provideMerge(Layer.mergeAll(layer$86, layer$20).pipe(Layer.provideMerge(layer$87), Layer.provideMerge(layer$88))))), Layer.provideMerge(layer$114));
136839
- const UsageLayerLive = layer$22.pipe(Layer.provide(layer$89.pipe(Layer.provide(layer$114))));
136840
- const RuntimeDependenciesLive = layer$21.pipe(Layer.provideMerge(RuntimeCoreDependenciesLive)).pipe(Layer.provideMerge(UsageLayerLive), Layer.provideMerge(layer$11), Layer.provideMerge(layer$10), Layer.provideMerge(layer$9), Layer.provideMerge(layer$76), Layer.provideMerge(layer$78), Layer.provideMerge(layer$77), Layer.provide(layer$118));
138202
+ const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(layer$25), 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$86)), Layer.provideMerge(layer$80), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(layer$58), Layer.provideMerge(Layer.mergeAll(layer$57, layer$70.pipe(Layer.provide(layer$82)), layer$72, layer$67)), Layer.provideMerge(layer$52), Layer.provideMerge(layer$53), Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$56)).pipe(Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(AntigravityInstallation.layer), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$60), Layer.provideMerge(layer$90.pipe(Layer.provide(layer$115))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$81), Layer.provideMerge(layer$66), Layer.provideMerge(remoteComputerUseBindingsLayer.pipe(Layer.provide(layer$86))), Layer.provideMerge(layer$74), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$5.pipe(Layer.provideMerge(LinearClientLive), Layer.provideMerge(Layer.mergeAll(LinearMcpClientLive, TrackerSecretsLive)), Layer.provideMerge(Layer.mergeAll(layer$87, layer$21).pipe(Layer.provideMerge(layer$88), Layer.provideMerge(layer$89))))), Layer.provideMerge(layer$115));
138203
+ const UsageLayerLive = layer$23.pipe(Layer.provide(layer$90.pipe(Layer.provide(layer$115))));
138204
+ const RuntimeDependenciesLive = layer$22.pipe(Layer.provideMerge(RuntimeCoreDependenciesLive)).pipe(Layer.provideMerge(UsageLayerLive), Layer.provideMerge(layer$12), Layer.provideMerge(layer$11), Layer.provideMerge(layer$10), Layer.provideMerge(layer$77), Layer.provideMerge(layer$79), Layer.provideMerge(layer$78), Layer.provide(layer$119));
136841
138205
  /**
136842
138206
  * Hub asset sync.
136843
138207
  *
@@ -136845,9 +138209,9 @@ const RuntimeDependenciesLive = layer$21.pipe(Layer.provideMerge(RuntimeCoreDepe
136845
138209
  * not-configured shape, and every call reports that rather than failing — so
136846
138210
  * this costs a server with no hub nothing but a layer.
136847
138211
  */
136848
- const AssetSyncLive = layer$83.pipe(Layer.provideMerge(layer$84), Layer.provideMerge(layer$85));
136849
- const RuntimeServicesLive = layer$69.pipe(Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
136850
- const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(pullRequestHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer$3.pipe(Layer.provide(layer$5)), layer$64, layer).pipe(Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionThreadRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$63), Layer.provide(layer$24), Layer.provide(PullRequestServiceLive), Layer.provide(layer$19), Layer.provide(layer$74), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
138212
+ const AssetSyncLive = layer$84.pipe(Layer.provideMerge(layer$85), Layer.provideMerge(layer$86));
138213
+ const RuntimeServicesLive = layer$69.pipe(Layer.provideMerge(layer$1), Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
138214
+ const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(pullRequestHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer$4.pipe(Layer.provide(layer$6)), layer$65, layer).pipe(Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionThreadRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$64), Layer.provide(layer$25), Layer.provide(PullRequestServiceLive), Layer.provide(layer$20), Layer.provide(layer$75), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
136851
138215
  const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
136852
138216
  const config = yield* ServerConfig$1;
136853
138217
  yield* fixPath();
@@ -136890,7 +138254,7 @@ const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
136890
138254
  cause,
136891
138255
  servePort: configured.servePort
136892
138256
  }))) : Effect.void)) : Layer.empty;
136893
- const fullyProvisionedServerLayer = Layer.mergeAll(HttpRouter.serve(makeRoutesLayer, { disableLogger: !config.logWebSocketEvents }), httpListeningLayer, runtimeStateLayer, tailscaleServeLayer, layer$1, layer$2).pipe(Layer.provideMerge(RuntimeServicesLive), Layer.provideMerge(HttpResponseCompressionLive), Layer.provideMerge(HttpServerLive), Layer.provide(ObservabilityLive), Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(layer$50));
138257
+ const fullyProvisionedServerLayer = Layer.mergeAll(HttpRouter.serve(makeRoutesLayer, { disableLogger: !config.logWebSocketEvents }), httpListeningLayer, runtimeStateLayer, tailscaleServeLayer, layer$2, layer$3).pipe(Layer.provideMerge(RuntimeServicesLive), Layer.provideMerge(HttpResponseCompressionLive), Layer.provideMerge(HttpServerLive), Layer.provide(ObservabilityLive), Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(layer$51));
136894
138258
  return Layer.unwrap(Effect.gen(function* () {
136895
138259
  yield* acquireServerInstanceLock(config.stateDir);
136896
138260
  return fullyProvisionedServerLayer;
@@ -137192,11 +138556,11 @@ function describeServiceUpdateState(state) {
137192
138556
  //#endregion
137193
138557
  //#region src/cli/service.ts
137194
138558
  const isBootServiceCommandError = Schema$1.is(BootServiceCommandError);
137195
- const bootServiceLayer = (config) => layer$75({
138559
+ const bootServiceLayer = (config) => layer$76({
137196
138560
  baseDir: config.baseDir,
137197
138561
  logsDir: config.logsDir,
137198
138562
  cliVersion: version$1
137199
- }).pipe(Layer.provide(layer$81));
138563
+ }).pipe(Layer.provide(layer$82));
137200
138564
  /**
137201
138565
  * The update installed a build that never reported itself listening.
137202
138566
  *
@@ -137322,7 +138686,7 @@ const runServiceCommand = Effect.fn("cli.service.run")(function* (flags, run, pr
137322
138686
  const baseDirNotice = yield* describeNonDefaultBaseDir(config.baseDir);
137323
138687
  if (baseDirNotice !== null) yield* Console.error(baseDirNotice);
137324
138688
  if (prepare !== void 0) yield* prepare(config);
137325
- return yield* run.pipe(Effect.tapError((error) => isBootServiceCommandError(error) ? BootService.pipe(Effect.flatMap((service) => Console.error(`Details: ${service.logPath}`))) : Effect.void), Effect.provide(Layer.mergeAll(bootServiceLayer(config), layer$81)));
138689
+ return yield* run.pipe(Effect.tapError((error) => isBootServiceCommandError(error) ? BootService.pipe(Effect.flatMap((service) => Console.error(`Details: ${service.logPath}`))) : Effect.void), Effect.provide(Layer.mergeAll(bootServiceLayer(config), layer$82)));
137326
138690
  });
137327
138691
  /**
137328
138692
  * Options the installed unit carries, rather than ones this command consumes.
@@ -137489,7 +138853,7 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
137489
138853
  ]));
137490
138854
  //#endregion
137491
138855
  //#region src/bin.ts
137492
- const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$118, FetchHttpClient.layer);
138856
+ const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$119, FetchHttpClient.layer);
137493
138857
  /**
137494
138858
  * Prints an update reminder before dispatching, at most once a day.
137495
138859
  *