@p4code/cli 0.4.16 → 0.4.18

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
@@ -260,7 +260,7 @@ const make$115 = () => {
260
260
  const layer$118 = Layer.sync(NetService, make$115);
261
261
  //#endregion
262
262
  //#region package.json
263
- var version$1 = "0.4.16";
263
+ var version$1 = "0.4.18";
264
264
  //#endregion
265
265
  //#region src/config.ts
266
266
  /**
@@ -1549,6 +1549,7 @@ const ServerSelfUpdateCapability = Schema$1.Literals([
1549
1549
  "desktop-managed"
1550
1550
  ]);
1551
1551
  const ExecutionEnvironmentCapabilities = Schema$1.Struct({
1552
+ computerUseControls: Schema$1.optionalKey(Schema$1.Boolean),
1552
1553
  usagePriceOverrides: Schema$1.optionalKey(Schema$1.Boolean),
1553
1554
  projectAutoPull: Schema$1.optionalKey(Schema$1.Boolean),
1554
1555
  sharedProjectModelDefault: Schema$1.optionalKey(Schema$1.Boolean),
@@ -9794,6 +9795,11 @@ const LoadBalancingWeights = Schema$1.Record(TrimmedNonEmptyString, Schema$1.Int
9794
9795
  minimum: 0,
9795
9796
  maximum: 100
9796
9797
  })));
9798
+ const ComputerUsePermissionHosts = Schema$1.Record(TrimmedNonEmptyString, Schema$1.Struct({
9799
+ environmentId: TrimmedNonEmptyString,
9800
+ hostId: TrimmedNonEmptyString,
9801
+ label: Schema$1.optionalKey(TrimmedNonEmptyString)
9802
+ }));
9797
9803
  const ClientSettingsSchema = Schema$1.Struct({
9798
9804
  loadBalancingEnabled: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
9799
9805
  loadBalancingWeights: LoadBalancingWeights.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
@@ -9805,6 +9811,7 @@ const ClientSettingsSchema = Schema$1.Struct({
9805
9811
  confirmThreadDelete: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
9806
9812
  dismissedProviderUpdateNotificationKeys: Schema$1.Array(TrimmedNonEmptyString).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
9807
9813
  diffIgnoreWhitespace: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
9814
+ computerUsePermissionHosts: ComputerUsePermissionHosts.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
9808
9815
  environmentIdentificationMode: EnvironmentIdentificationMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_ENVIRONMENT_IDENTIFICATION_MODE))),
9809
9816
  glassOpacity: GlassOpacity.pipe(Schema$1.withDecodingDefault(Effect.succeed(80))),
9810
9817
  favorites: Schema$1.Array(Schema$1.Struct({
@@ -10389,6 +10396,7 @@ Schema$1.Struct({
10389
10396
  confirmThreadArchive: Schema$1.optionalKey(Schema$1.Boolean),
10390
10397
  confirmThreadDelete: Schema$1.optionalKey(Schema$1.Boolean),
10391
10398
  diffIgnoreWhitespace: Schema$1.optionalKey(Schema$1.Boolean),
10399
+ computerUsePermissionHosts: Schema$1.optionalKey(ComputerUsePermissionHosts),
10392
10400
  environmentIdentificationMode: Schema$1.optionalKey(EnvironmentIdentificationMode),
10393
10401
  glassOpacity: Schema$1.optionalKey(GlassOpacity),
10394
10402
  favorites: Schema$1.optionalKey(Schema$1.Array(Schema$1.Struct({
@@ -13338,6 +13346,9 @@ const ComputerUseHost = Schema$1.Struct({
13338
13346
  nativeApps: Schema$1.Boolean
13339
13347
  });
13340
13348
  const ComputerUseSession = Schema$1.Struct({
13349
+ generationId: Schema$1.optional(TrimmedNonEmptyString),
13350
+ threadTitle: Schema$1.optional(Schema$1.String),
13351
+ environmentLabel: Schema$1.optional(Schema$1.String),
13341
13352
  environmentId: EnvironmentId,
13342
13353
  threadId: ThreadId,
13343
13354
  providerSessionId: TrimmedNonEmptyString,
@@ -13364,7 +13375,9 @@ const ComputerUseHostRequest = Schema$1.Struct({
13364
13375
  operation: Schema$1.Literals([
13365
13376
  "js",
13366
13377
  "js_reset",
13367
- "close"
13378
+ "close",
13379
+ "stop",
13380
+ "resume"
13368
13381
  ]),
13369
13382
  input: Schema$1.optional(ComputerUseJsInput)
13370
13383
  });
@@ -13376,10 +13389,15 @@ var ComputerUseError = class extends Schema$1.TaggedErrorClass()("ComputerUseErr
13376
13389
  "busy",
13377
13390
  "timeout",
13378
13391
  "reset",
13379
- "execution-failed"
13392
+ "execution-failed",
13393
+ "stopped"
13380
13394
  ]),
13381
13395
  detail: Schema$1.String
13382
- }) {};
13396
+ }) {
13397
+ get message() {
13398
+ return this.detail;
13399
+ }
13400
+ };
13383
13401
  const ComputerUseHostRegistration = Schema$1.Struct({
13384
13402
  hostId: TrimmedNonEmptyString,
13385
13403
  environmentId: EnvironmentId,
@@ -13406,6 +13424,30 @@ const ComputerUseHostResponse = Schema$1.Struct({
13406
13424
  error: ComputerUseError
13407
13425
  })])
13408
13426
  });
13427
+ const ComputerUseHostReport = Schema$1.Struct({
13428
+ hostId: TrimmedNonEmptyString,
13429
+ connectionId: TrimmedNonEmptyString,
13430
+ session: ComputerUseSession,
13431
+ state: Schema$1.Literals([
13432
+ "active",
13433
+ "closed",
13434
+ "stopped"
13435
+ ])
13436
+ });
13437
+ const ComputerUseControlState = Schema$1.Struct({
13438
+ session: ComputerUseSession,
13439
+ host: ComputerUseHost,
13440
+ state: Schema$1.Literals([
13441
+ "active",
13442
+ "stopping",
13443
+ "stopped",
13444
+ "stop-unconfirmed"
13445
+ ])
13446
+ });
13447
+ const ComputerUseControlInput = Schema$1.Struct({
13448
+ threadId: ThreadId,
13449
+ action: Schema$1.Literals(["stop", "resume"])
13450
+ });
13409
13451
  const ComputerUseEnrollmentRequest = Schema$1.Struct({
13410
13452
  environmentId: EnvironmentId,
13411
13453
  desktopSessionId: TrimmedNonEmptyString,
@@ -13465,6 +13507,8 @@ const WS_METHODS = {
13465
13507
  previewReportStatus: "preview.reportStatus",
13466
13508
  computerUseConnect: "computerUse.connect",
13467
13509
  computerUseList: "computerUse.list",
13510
+ computerUseControls: "computerUse.controls",
13511
+ computerUseControl: "computerUse.control",
13468
13512
  computerUseRespond: "computerUse.respond",
13469
13513
  previewAutomationConnect: "previewAutomation.connect",
13470
13514
  previewAutomationRespond: "previewAutomation.respond",
@@ -14488,6 +14532,16 @@ const WsComputerUseListRpc = Rpc.make(WS_METHODS.computerUseList, {
14488
14532
  success: Schema$1.Array(ComputerUseHost),
14489
14533
  error: EnvironmentAuthorizationError
14490
14534
  });
14535
+ const WsComputerUseControlsRpc = Rpc.make(WS_METHODS.computerUseControls, {
14536
+ payload: Schema$1.Struct({}),
14537
+ success: Schema$1.Array(ComputerUseControlState),
14538
+ error: EnvironmentAuthorizationError,
14539
+ stream: true
14540
+ });
14541
+ const WsComputerUseControlRpc = Rpc.make(WS_METHODS.computerUseControl, {
14542
+ payload: ComputerUseControlInput,
14543
+ error: Schema$1.Union([ComputerUseError, EnvironmentAuthorizationError])
14544
+ });
14491
14545
  const WsComputerUseRespondRpc = Rpc.make(WS_METHODS.computerUseRespond, {
14492
14546
  payload: ComputerUseHostResponse,
14493
14547
  error: Schema$1.Union([ComputerUseError, EnvironmentAuthorizationError])
@@ -14594,7 +14648,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
14594
14648
  error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
14595
14649
  stream: true
14596
14650
  });
14597
- 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, 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);
14651
+ 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, 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);
14598
14652
  //#endregion
14599
14653
  //#region ../../packages/contracts/src/deviceCommand.ts
14600
14654
  const MAX_COORDINATE = 32768;
@@ -44553,6 +44607,7 @@ const make$88 = Effect.gen(function* () {
44553
44607
  connectionProbe: true,
44554
44608
  pullRequests: true,
44555
44609
  hostResources: true,
44610
+ computerUseControls: true,
44556
44611
  deviceSessionLeases: true,
44557
44612
  deviceActionLeases: true,
44558
44613
  pullRequestStackActions: true,
@@ -53049,6 +53104,7 @@ const COMPUTER_USE_HELPER_CREDENTIAL_TTL_MS = 300 * 1e3;
53049
53104
  const HELPER_CREDENTIAL_BYTES = 32;
53050
53105
  const MAX_PENDING_REQUESTS = 64;
53051
53106
  const TRANSPORT_GRACE_MS = 2e3;
53107
+ const COMPUTER_USE_CONTROL_ACK_TIMEOUT_MS = 5e3;
53052
53108
  const scopeKey = (scope) => JSON.stringify([
53053
53109
  scope.environmentId,
53054
53110
  scope.threadId,
@@ -53058,14 +53114,26 @@ const scopeKey = (scope) => JSON.stringify([
53058
53114
  const hostSession = (scope) => ({
53059
53115
  environmentId: scope.environmentId,
53060
53116
  threadId: scope.threadId,
53117
+ ...scope.threadTitle ? { threadTitle: scope.threadTitle } : {},
53061
53118
  providerInstanceId: scope.providerInstanceId,
53062
53119
  providerSessionId: scope.providerSessionId
53063
53120
  });
53064
53121
  var ComputerUseBroker = class extends Context.Service()("@p4code/cli/mcp/ComputerUseBroker") {};
53065
53122
  const layer$67 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53123
+ const serviceScope = yield* Scope.Scope;
53066
53124
  const crypto = yield* Crypto.Crypto;
53067
- const environmentId = yield* (yield* ServerEnvironment).getEnvironmentId;
53125
+ const environment = yield* ServerEnvironment;
53126
+ const environmentId = yield* environment.getEnvironmentId;
53068
53127
  const admission = yield* Semaphore.make(1);
53128
+ const controlAdmission = yield* Semaphore.make(1);
53129
+ const states = /* @__PURE__ */ new Map();
53130
+ const stoppedThreads = /* @__PURE__ */ new Set();
53131
+ const controls = yield* SubscriptionRef.make([]);
53132
+ const publish = () => SubscriptionRef.set(controls, [...states.values()]);
53133
+ const stopped = () => new ComputerUseError({
53134
+ reason: "stopped",
53135
+ detail: "Computer Use was stopped by the user. Ask the user to allow Computer Use again before retrying. Resetting cannot restore access."
53136
+ });
53069
53137
  const connections = /* @__PURE__ */ new Map();
53070
53138
  const helperCredentials = /* @__PURE__ */ new Map();
53071
53139
  const leases = /* @__PURE__ */ new Map();
@@ -53084,6 +53152,15 @@ const layer$67 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53084
53152
  detail: "Computer Use host disconnected. Reset before selecting another host."
53085
53153
  }));
53086
53154
  }
53155
+ for (const [key, state] of states) {
53156
+ if (state.host.connectionId !== connection.host.connectionId) continue;
53157
+ if (state.state === "active") states.delete(key);
53158
+ else if (state.state === "stopping") states.set(key, {
53159
+ ...state,
53160
+ state: "stop-unconfirmed"
53161
+ });
53162
+ }
53163
+ yield* publish();
53087
53164
  yield* Queue.shutdown(connection.queue);
53088
53165
  });
53089
53166
  const revokeHelper = Effect.fn("ComputerUseBroker.revokeHelper")(function* (credential) {
@@ -53143,12 +53220,119 @@ const layer$67 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53143
53220
  requestId,
53144
53221
  hostId: connection.host.hostId,
53145
53222
  connectionId: connection.host.connectionId,
53146
- session: hostSession(scope),
53223
+ session: states.get(key)?.session ?? hostSession(scope),
53147
53224
  operation: "close"
53148
53225
  }
53149
53226
  });
53150
53227
  }
53151
53228
  }, admission.withPermits(1));
53229
+ const stopThread = Effect.fn("ComputerUseBroker.stopThread")(function* (threadId) {
53230
+ stoppedThreads.add(threadId);
53231
+ for (const [key, state] of states) {
53232
+ if (state.session.threadId !== threadId) continue;
53233
+ if (state.state !== "stopped") states.set(key, {
53234
+ ...state,
53235
+ state: "stopping"
53236
+ });
53237
+ leases.delete(key);
53238
+ for (const [id, request] of pending) {
53239
+ if (request.scopeKey !== key) continue;
53240
+ pending.delete(id);
53241
+ yield* Deferred.fail(request.completion, stopped());
53242
+ }
53243
+ }
53244
+ yield* publish();
53245
+ });
53246
+ const sendControl = Effect.fn("ComputerUseBroker.sendControl")(function* (state, action) {
53247
+ const connection = connections.get(state.host.hostId);
53248
+ if (!connection || action === "stop" && connection.host.connectionId !== state.host.connectionId) {
53249
+ if (action === "resume") return yield* new ComputerUseError({
53250
+ reason: "disconnected",
53251
+ detail: "Reconnect this Mac before allowing Computer Use again. Access remains blocked."
53252
+ });
53253
+ if (state.state === "stopped") return;
53254
+ for (const [key, entry] of states) if (entry.session.threadId === state.session.threadId && entry.host.connectionId === state.host.connectionId) states.set(key, {
53255
+ ...entry,
53256
+ state: "stop-unconfirmed"
53257
+ });
53258
+ yield* publish();
53259
+ return yield* new ComputerUseError({
53260
+ reason: "disconnected",
53261
+ detail: "Computer Use host is unreachable. Access is blocked, but the Mac has not confirmed Stop."
53262
+ });
53263
+ }
53264
+ const requestId = yield* crypto.randomUUIDv4.pipe(Effect.orDie);
53265
+ const completion = yield* Deferred.make();
53266
+ pending.set(requestId, {
53267
+ connection,
53268
+ completion,
53269
+ scopeKey: requestId
53270
+ });
53271
+ yield* Effect.gen(function* () {
53272
+ yield* Queue.offer(connection.queue, {
53273
+ type: "request",
53274
+ request: {
53275
+ requestId,
53276
+ hostId: connection.host.hostId,
53277
+ connectionId: connection.host.connectionId,
53278
+ session: state.session,
53279
+ operation: action
53280
+ }
53281
+ });
53282
+ yield* Deferred.await(completion).pipe(Effect.timeout(COMPUTER_USE_CONTROL_ACK_TIMEOUT_MS));
53283
+ }).pipe(Effect.catch(() => disconnect(connection).pipe(Effect.andThen(Effect.fail(new ComputerUseError({
53284
+ reason: "disconnected",
53285
+ detail: "Computer Use host did not confirm the control change. Access remains blocked. Check the Mac before allowing control again."
53286
+ }))))), Effect.ensuring(Effect.sync(() => pending.delete(requestId))));
53287
+ });
53288
+ const report = Effect.fn("ComputerUseBroker.report")(function* (owner, report) {
53289
+ const authorization = yield* authorize(owner);
53290
+ const connection = connections.get(report.hostId);
53291
+ if (!connection || connection.authorization !== authorization || connection.host.connectionId !== report.connectionId || report.session.environmentId !== environmentId) return yield* deny();
53292
+ const key = scopeKey(report.session);
53293
+ const state = states.get(key);
53294
+ if (!state || state.host.connectionId !== report.connectionId || state.session.generationId !== report.session.generationId) return;
53295
+ if (report.state === "stopped") {
53296
+ const others = [...states.values()].filter((entry) => entry.session.threadId === report.session.threadId && entry.state === "active" && entry.host.connectionId !== report.connectionId);
53297
+ yield* stopThread(report.session.threadId);
53298
+ for (const other of new Map(others.map((entry) => [entry.host.connectionId, entry])).values()) yield* sendControl(other, "stop").pipe(Effect.ignore, Effect.forkIn(serviceScope));
53299
+ for (const [entryKey, entry] of states) if (entry.session.threadId === report.session.threadId && entry.host.connectionId === report.connectionId) states.set(entryKey, {
53300
+ ...entry,
53301
+ state: "stopped"
53302
+ });
53303
+ } else if (report.state === "closed") {
53304
+ leases.delete(key);
53305
+ if (state.state === "active") states.delete(key);
53306
+ else states.set(key, {
53307
+ ...state,
53308
+ state: "stopped"
53309
+ });
53310
+ }
53311
+ if (report.state !== "active") yield* publish();
53312
+ }, admission.withPermits(1));
53313
+ const control = Effect.fn("ComputerUseBroker.control")(function* (input) {
53314
+ const targets = yield* admission.withPermits(1)(Effect.gen(function* () {
53315
+ const targets = [...states.values()].filter((state) => state.session.threadId === input.threadId);
53316
+ if (input.action === "stop" && targets.length > 0) yield* stopThread(input.threadId);
53317
+ return targets;
53318
+ }));
53319
+ const hosts = new Map(targets.map((state) => [state.host.connectionId, state]));
53320
+ let failure;
53321
+ yield* Effect.forEach([...hosts.values()], (state) => sendControl(state, input.action).pipe(Effect.catch((error) => Effect.sync(() => {
53322
+ failure = error;
53323
+ }))), { concurrency: "unbounded" });
53324
+ if (failure) return yield* failure;
53325
+ if (input.action === "resume") stoppedThreads.delete(input.threadId);
53326
+ for (const [key, state] of states) {
53327
+ if (state.session.threadId !== input.threadId) continue;
53328
+ if (input.action === "resume") states.delete(key);
53329
+ else states.set(key, {
53330
+ ...state,
53331
+ state: "stopped"
53332
+ });
53333
+ }
53334
+ yield* publish();
53335
+ }, controlAdmission.withPermits(1), Effect.uninterruptible);
53152
53336
  return ComputerUseBroker.of({
53153
53337
  attachVerifiedHelper: (binding) => Effect.gen(function* () {
53154
53338
  if (binding.host.environmentId !== environmentId || !binding.helperIdentity || !binding.desktopSessionId) return yield* deny();
@@ -53210,6 +53394,9 @@ const layer$67 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53210
53394
  expiresAt
53211
53395
  };
53212
53396
  }, admission.withPermits(1)),
53397
+ report,
53398
+ control,
53399
+ controls: SubscriptionRef.changes(controls),
53213
53400
  list: Effect.sync(() => [...connections.values()].map((entry) => entry.host)),
53214
53401
  connect: (owner, host) => authorize(owner).pipe(Effect.as(Stream.unwrap(Effect.acquireRelease(admission.withPermits(1)(acquire(owner, host)), (connection) => revokeHelper(connection.owner)).pipe(Effect.map((connection) => Stream.fromQueue(connection.queue)))))),
53215
53402
  respond: Effect.fn("ComputerUseBroker.respond")(function* (owner, response) {
@@ -53225,19 +53412,20 @@ const layer$67 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53225
53412
  invoke: (scope, input, hostId) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
53226
53413
  const { requestId, connection, completion, request } = yield* admission.withPermits(1)(Effect.gen(function* () {
53227
53414
  if (scope.authorizationLifetime?.revoked === true || !scope.capabilities.has("computer-use") || scope.environmentId !== environmentId) return yield* deny();
53228
- if (!scope.computerUseHostId || hostId !== void 0 && hostId !== scope.computerUseHostId) return yield* deny();
53229
- const grantedHostId = scope.computerUseHostId;
53415
+ if (scope.computerUseHostId && hostId !== void 0 && hostId !== scope.computerUseHostId) return yield* deny();
53416
+ if (stoppedThreads.has(scope.threadId)) return yield* stopped();
53417
+ const grantedHostId = scope.computerUseHostId ?? hostId;
53230
53418
  const key = scopeKey(scope);
53231
53419
  const existing = leases.get(key);
53232
53420
  if (existing && (connections.get(existing.host.hostId) !== existing || hostId !== void 0 && hostId !== existing.host.hostId)) return yield* new ComputerUseError({
53233
53421
  reason: "disconnected",
53234
53422
  detail: "Reset Computer Use before changing host connections."
53235
53423
  });
53236
- const candidates = [...connections.values()].filter((entry) => (entry.host.browser || entry.host.nativeApps) && entry.host.hostId === grantedHostId);
53424
+ const candidates = [...connections.values()].filter((entry) => (entry.host.browser || entry.host.nativeApps) && (grantedHostId === void 0 || entry.host.hostId === grantedHostId));
53237
53425
  const connection = existing ?? (candidates.length === 1 ? candidates[0] : void 0);
53238
53426
  if (!connection) return yield* new ComputerUseError({
53239
53427
  reason: "unavailable",
53240
- detail: candidates.length > 1 ? "Select a Computer Use host explicitly." : "No Computer Use host is available."
53428
+ detail: candidates.length > 1 ? `Select a Computer Use host with hostId: ${candidates.map((entry) => `${entry.host.label} (${entry.host.hostId})`).join(", ")}.` : "No Computer Use host is available."
53241
53429
  });
53242
53430
  if (pending.size >= MAX_PENDING_REQUESTS || [...pending.values()].some((request) => request.scopeKey === key)) return yield* new ComputerUseError({
53243
53431
  reason: "busy",
@@ -53246,6 +53434,20 @@ const layer$67 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53246
53434
  yield* authorize(connection.owner);
53247
53435
  const requestId = yield* crypto.randomUUIDv4.pipe(Effect.orDie);
53248
53436
  const completion = yield* Deferred.make();
53437
+ const descriptor = yield* environment.getDescriptor;
53438
+ const session = existing ? states.get(key)?.session ?? hostSession(scope) : {
53439
+ ...hostSession(scope),
53440
+ environmentLabel: descriptor.label,
53441
+ generationId: requestId
53442
+ };
53443
+ if (!existing) {
53444
+ states.set(key, {
53445
+ session,
53446
+ host: connection.host,
53447
+ state: "active"
53448
+ });
53449
+ yield* publish();
53450
+ }
53249
53451
  leases.set(key, connection);
53250
53452
  pending.set(requestId, {
53251
53453
  connection,
@@ -53260,7 +53462,7 @@ const layer$67 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53260
53462
  requestId,
53261
53463
  hostId: connection.host.hostId,
53262
53464
  connectionId: connection.host.connectionId,
53263
- session: hostSession(scope),
53465
+ session,
53264
53466
  operation: "js",
53265
53467
  input
53266
53468
  }
@@ -53285,7 +53487,7 @@ const layer$67 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
53285
53487
  pending.delete(requestId);
53286
53488
  })));
53287
53489
  })),
53288
- reset: (scope) => scope.authorizationLifetime?.revoked !== true && scope.environmentId === environmentId && scope.capabilities.has("computer-use") ? revoke(scope) : Effect.fail(deny()),
53490
+ reset: (scope) => scope.authorizationLifetime?.revoked !== true && scope.environmentId === environmentId && scope.capabilities.has("computer-use") ? stoppedThreads.has(scope.threadId) ? Effect.fail(stopped()) : revoke(scope) : Effect.fail(deny()),
53289
53491
  revoke,
53290
53492
  invalidate: (scope) => Effect.sync(() => {
53291
53493
  if (scope.authorizationLifetime) scope.authorizationLifetime.revoked = true;
@@ -53388,6 +53590,14 @@ const respond = HttpRouter.add("POST", "/internal/computer-use/respond", Effect.
53388
53590
  yield* broker.respond(bearer(request), response);
53389
53591
  return HttpServerResponse.empty({ status: 204 });
53390
53592
  }).pipe(Effect.catch(failure$1)));
53593
+ const decodeHostReport = Schema$1.decodeUnknownEffect(ComputerUseHostReport);
53594
+ const report = HttpRouter.add("POST", "/internal/computer-use/report", Effect.gen(function* () {
53595
+ const request = yield* HttpServerRequest.HttpServerRequest;
53596
+ const broker = yield* ComputerUseBroker;
53597
+ const report = yield* decodeHostReport(yield* request.json);
53598
+ yield* broker.report(bearer(request), report);
53599
+ return HttpServerResponse.empty({ status: 204 });
53600
+ }).pipe(Effect.catch(failure$1)));
53391
53601
  const environment = HttpRouter.add("GET", "/internal/computer-use/environment", Effect.gen(function* () {
53392
53602
  const request = yield* HttpServerRequest.HttpServerRequest;
53393
53603
  const binding = yield* resolveDesktopBinding(bearer(request));
@@ -53415,7 +53625,7 @@ const renew = HttpRouter.add("POST", "/internal/computer-use/renew", Effect.gen(
53415
53625
  if (binding.trust) yield* (yield* RemoteComputerUseBindings).touch(bearer(request));
53416
53626
  return HttpServerResponse.empty({ status: 204 });
53417
53627
  }).pipe(Effect.catch(failure$1)));
53418
- const layer$66 = Layer.mergeAll(exchange, hubForward, hubExchange, connect, respond, environment, renew).pipe(Layer.provide(remoteComputerUseBindingsLayer));
53628
+ const layer$66 = Layer.mergeAll(exchange, hubForward, hubExchange, connect, respond, report, environment, renew).pipe(Layer.provide(remoteComputerUseBindingsLayer));
53419
53629
  //#endregion
53420
53630
  //#region src/device/DeviceHubSocketProtocol.ts
53421
53631
  const MAX_DEVICE_INPUT_BYTES = 8 * 1024;
@@ -63291,7 +63501,7 @@ function formatAskUserQuestionAnswers(answers) {
63291
63501
  */
63292
63502
  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.`;
63293
63503
  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.`;
63294
- 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. ${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.`;
63504
+ 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: inspect the task and available project context, plan the work, then direct the first actionable phase to the idle builder through thread_advise. Include the original requirements, constraints, and authorized delivery scope in that direction. 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.`;
63295
63505
  /**
63296
63506
  * The one-line stand-in for the full block on a message whose session already
63297
63507
  * carries the role instructions.
@@ -124607,6 +124817,8 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
124607
124817
  [WS_METHODS.previewReportStatus, AuthOrchestrationOperateScope],
124608
124818
  [WS_METHODS.computerUseConnect, AuthOrchestrationOperateScope],
124609
124819
  [WS_METHODS.computerUseList, AuthOrchestrationReadScope],
124820
+ [WS_METHODS.computerUseControls, AuthOrchestrationReadScope],
124821
+ [WS_METHODS.computerUseControl, AuthOrchestrationOperateScope],
124610
124822
  [WS_METHODS.computerUseRespond, AuthOrchestrationOperateScope],
124611
124823
  [WS_METHODS.previewAutomationConnect, AuthOrchestrationOperateScope],
124612
124824
  [WS_METHODS.previewAutomationRespond, AuthOrchestrationOperateScope],
@@ -125041,6 +125253,18 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
125041
125253
  branch: worktree.worktree.refName,
125042
125254
  worktreePath: targetWorktreePath
125043
125255
  });
125256
+ if (!bootstrap.createThread) {
125257
+ const snapshot = yield* projectionSnapshotQuery.getCommandReadModel();
125258
+ const pair = snapshot.threadPairs?.find((candidate) => candidate.detachedAt === null && candidate.watcherThreadId === command.threadId);
125259
+ const builder = pair && snapshot.threads.find((thread) => thread.id === pair.implementerThreadId && thread.deletedAt === null);
125260
+ if (builder && builder.latestTurn === null && builder.messages.length === 0 && builder.worktreePath === null) yield* orchestrationEngine.dispatch({
125261
+ type: "thread.meta.update",
125262
+ commandId: yield* serverCommandId("bootstrap-builder-meta-update"),
125263
+ threadId: builder.id,
125264
+ branch: worktree.worktree.refName,
125265
+ worktreePath: targetWorktreePath
125266
+ });
125267
+ }
125044
125268
  yield* refreshGitStatus(targetWorktreePath);
125045
125269
  }
125046
125270
  yield* runSetupProgram();
@@ -125632,6 +125856,8 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
125632
125856
  [WS_METHODS.previewList]: (input) => observeRpcEffect$1(WS_METHODS.previewList, previewManager.list(input), { "rpc.aggregate": "preview" }),
125633
125857
  [WS_METHODS.previewReportStatus]: (input) => observeRpcEffect$1(WS_METHODS.previewReportStatus, previewManager.reportStatus(input), { "rpc.aggregate": "preview" }),
125634
125858
  [WS_METHODS.computerUseList]: () => computerUseBroker.list,
125859
+ [WS_METHODS.computerUseControls]: () => computerUseBroker.controls,
125860
+ [WS_METHODS.computerUseControl]: (input) => computerUseBroker.control(input),
125635
125861
  [WS_METHODS.computerUseConnect]: () => Effect.fail(new ComputerUseError({
125636
125862
  reason: "denied",
125637
125863
  detail: "Ordinary environment clients cannot register Computer Use helpers."
@@ -126105,7 +126331,7 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
126105
126331
  const providerSessionId = yield* crypto.randomUUIDv4.pipe(Effect.orDie);
126106
126332
  const rawToken = yield* crypto.randomBytes(32).pipe(Effect.map(tokenFromBytes), Effect.orDie);
126107
126333
  const tokenHash = yield* hashToken(rawToken);
126108
- const computerUseToken = request.computerUseHostId ? yield* crypto.randomBytes(32).pipe(Effect.map(tokenFromBytes), Effect.orDie) : void 0;
126334
+ const computerUseToken = request.computerUseAccess || request.computerUseHostId ? yield* crypto.randomBytes(32).pipe(Effect.map(tokenFromBytes), Effect.orDie) : void 0;
126109
126335
  const computerUseHash = computerUseToken ? yield* hashToken(computerUseToken) : void 0;
126110
126336
  const watchThreadIds = new Set((request.watchThreadIds ?? []).map((threadId) => ThreadId.make(threadId)));
126111
126337
  const adviseThreadIds = new Set((request.adviseThreadIds ?? []).map((threadId) => ThreadId.make(threadId)));
@@ -126127,6 +126353,7 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
126127
126353
  },
126128
126354
  environmentId,
126129
126355
  threadId,
126356
+ ...request.threadTitle ? { threadTitle: request.threadTitle } : {},
126130
126357
  providerSessionId,
126131
126358
  providerInstanceId: ProviderInstanceId.make(request.providerInstanceId),
126132
126359
  capabilities,
@@ -126144,12 +126371,12 @@ const makeWithOptions = Effect.fn("McpSessionRegistry.make")(function* (options
126144
126371
  controlThreadIds: /* @__PURE__ */ new Set(),
126145
126372
  lastAliveAt: issuedAt
126146
126373
  });
126147
- if (computerUseHash && request.computerUseHostId) next.set(computerUseHash, {
126374
+ if (computerUseHash) next.set(computerUseHash, {
126148
126375
  tokenHash: computerUseHash,
126149
126376
  scope: {
126150
126377
  ...scopeWith(false, false, computerRevocation),
126151
126378
  capabilities: /* @__PURE__ */ new Set(["computer-use"]),
126152
- computerUseHostId: request.computerUseHostId
126379
+ ...request.computerUseHostId ? { computerUseHostId: request.computerUseHostId } : {}
126153
126380
  },
126154
126381
  controlThreadIds: /* @__PURE__ */ new Set(),
126155
126382
  lastAliveAt: issuedAt
@@ -126481,16 +126708,14 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
126481
126708
  const thread = yield* projectionQuery.value.getThreadShellById(threadId);
126482
126709
  return Option.isSome(thread) ? resolveProjectAgentBrowserAccess(settings, thread.value.projectId) : false;
126483
126710
  })), Effect.catch((cause) => Effect.logWarning("Could not read settings; withholding agent browser access.", { cause }).pipe(Effect.as(false))));
126484
- const computerUseHostId = yield* Effect.gen(function* () {
126485
- if ((yield* registry.getInstanceInfo(providerInstanceId)).driverKind === "codex" || Option.isNone(projectionQuery)) return void 0;
126486
- const thread = yield* projectionQuery.value.getThreadShellById(threadId);
126487
- return Option.isSome(thread) ? thread.value.computerUseHostId ?? void 0 : void 0;
126488
- }).pipe(Effect.catch(() => Effect.void));
126711
+ const computerUseAccess = yield* registry.getInstanceInfo(providerInstanceId).pipe(Effect.map((info) => info.driverKind !== "codex"), Effect.catch(() => Effect.succeed(false)));
126712
+ const threadTitle = Option.isSome(projectionQuery) ? yield* projectionQuery.value.getThreadShellById(threadId).pipe(Effect.map((thread) => Option.isSome(thread) ? thread.value.title : void 0), Effect.catch(() => Effect.succeed(void 0))) : void 0;
126489
126713
  const credential = yield* issueActiveMcpCredential({
126490
126714
  threadId,
126715
+ ...threadTitle ? { threadTitle } : {},
126491
126716
  providerInstanceId,
126492
126717
  browserAccess,
126493
- ...computerUseHostId ? { computerUseHostId } : {},
126718
+ computerUseAccess,
126494
126719
  ...watchThreadIds !== void 0 ? { watchThreadIds } : {},
126495
126720
  ...adviseThreadIds !== void 0 ? { adviseThreadIds } : {}
126496
126721
  });
@@ -127279,6 +127504,25 @@ const makeProviderSessionReaper = (options) => Effect.gen(function* () {
127279
127504
  const makeProviderSessionReaperLive = (options) => Layer.effect(ProviderSessionReaper, makeProviderSessionReaper(options));
127280
127505
  const ProviderSessionReaperLive = makeProviderSessionReaperLive();
127281
127506
  //#endregion
127507
+ //#region src/mcp/McpToolkit.ts
127508
+ const mcpInputSchema = (schema) => ({
127509
+ ...schema,
127510
+ type: "object"
127511
+ });
127512
+ const mcpToolkit = (toolkit) => Layer.effectDiscard(Effect.gen(function* () {
127513
+ const server = yield* McpServer.McpServer;
127514
+ yield* McpServer.registerToolkit(toolkit).pipe(Effect.provideService(McpServer.McpServer, {
127515
+ ...server,
127516
+ addTool: (options) => server.addTool({
127517
+ ...options,
127518
+ tool: new McpSchema.Tool({
127519
+ ...options.tool,
127520
+ inputSchema: mcpInputSchema(options.tool.inputSchema)
127521
+ })
127522
+ })
127523
+ }));
127524
+ }));
127525
+ //#endregion
127282
127526
  //#region src/mcp/toolkits/computerUse/tools.ts
127283
127527
  const ComputerUseJsTool = Tool.make("js", {
127284
127528
  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 getState(), activate(), click({elementId}), or type({elementId,text}) to replace a field value. Element IDs come from the latest app snapshot. List windows with app.windows(); capture a listed ID with app.screenshot(windowId), then nodeRepl.emitImage(image). 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.",
@@ -127320,7 +127564,7 @@ const register = Effect.fn("computerUse.register")(function* () {
127320
127564
  tool: new McpSchema.Tool({
127321
127565
  name: tool.name,
127322
127566
  description: Tool.getDescription(tool),
127323
- inputSchema: Tool.getJsonSchema(tool),
127567
+ inputSchema: mcpInputSchema(Tool.getJsonSchema(tool)),
127324
127568
  annotations: {
127325
127569
  readOnlyHint: false,
127326
127570
  destructiveHint: true,
@@ -127711,7 +127955,7 @@ const registerScreenshot = Effect.fn("device.registerScreenshot")(function* () {
127711
127955
  tool: new McpSchema.Tool({
127712
127956
  name: DeviceScreenshotTool.name,
127713
127957
  description: Tool.getDescription(DeviceScreenshotTool),
127714
- inputSchema: Tool.getJsonSchema(DeviceScreenshotTool),
127958
+ inputSchema: mcpInputSchema(Tool.getJsonSchema(DeviceScreenshotTool)),
127715
127959
  annotations: {
127716
127960
  readOnlyHint: true,
127717
127961
  destructiveHint: false,
@@ -127729,7 +127973,7 @@ const registerScreenshot = Effect.fn("device.registerScreenshot")(function* () {
127729
127973
  }))))
127730
127974
  });
127731
127975
  });
127732
- const DeviceToolkitRegistrationLive = Layer.mergeAll(McpServer.toolkit(DeviceStandardToolkit), Layer.effectDiscard(registerScreenshot())).pipe(Layer.provide(DeviceToolkitHandlersLive));
127976
+ const DeviceToolkitRegistrationLive = Layer.mergeAll(mcpToolkit(DeviceStandardToolkit), Layer.effectDiscard(registerScreenshot())).pipe(Layer.provide(DeviceToolkitHandlersLive));
127733
127977
  //#endregion
127734
127978
  //#region src/mcp/toolkits/preview/tools.ts
127735
127979
  const dependencies$1 = [McpInvocationContext, PreviewAutomationBroker];
@@ -128467,7 +128711,7 @@ const AskUserQuestionTool = Tool.make("ask_user_question", {
128467
128711
  ]
128468
128712
  }).annotate(Tool.Title, "Ask user question").annotate(Tool.Readonly, false).annotate(Tool.Destructive, false).annotate(Tool.Idempotent, false);
128469
128713
  const ThreadSpawnTool = Tool.make("thread_spawn", {
128470
- description: "Start a new agent thread and send it a first message, then return its id. Use it to hand a piece of work to a fresh thread - a subtask you just planned, a job that wants its own transcript. Set fusion true to create two new threads as a separate Fusion pair and start its builder with prompt, even when this thread already belongs to a pair. Set fusionWatcher true only for a supervisor to pair with the current thread. Both use saved Fusion role models unless overridden. Act on the user's request; no special command syntax is required. The new thread inherits this one's project, model, permission mode, interaction mode, compression and subagent policy, and every one of those can be set here instead - what you set is already in force for the first turn, so a thread never has to be corrected after it starts. Set modelSelection to run it on another model: give the model id, and leave instanceId out to keep this thread's provider instance. Once started it can be watched with thread_watch_events and changed later with thread_configure. A thread that was itself started this way cannot start another.",
128714
+ description: "Start a new agent thread and send it a first message, then return its id. Use it to hand a piece of work to a fresh thread - a subtask you just planned, a job that wants its own transcript. Set fusion true to create two new threads as a separate Fusion pair and send prompt to its supervisor, which plans and directs the builder later, even when this thread already belongs to a pair. Set fusionWatcher true only for a supervisor to pair with the current thread. Both use saved Fusion role models unless overridden. Act on the user's request; no special command syntax is required. The new thread inherits this one's project, model, permission mode, interaction mode, compression and subagent policy, and every one of those can be set here instead - what you set is already in force for the first turn, so a thread never has to be corrected after it starts. Set modelSelection to run it on another model: give the model id, and leave instanceId out to keep this thread's provider instance. Once started it can be watched with thread_watch_events and changed later with thread_configure. A thread that was itself started this way cannot start another.",
128471
128715
  parameters: ThreadSpawnInput,
128472
128716
  success: ThreadSpawnResult,
128473
128717
  failure: ThreadControlToolError,
@@ -128871,21 +129115,21 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
128871
129115
  yield* dispatchControl({
128872
129116
  type: "thread.turn.start",
128873
129117
  commandId: yield* newCommandId,
128874
- threadId,
129118
+ threadId: fusionPair?.watcherThreadId ?? threadId,
128875
129119
  message: {
128876
129120
  messageId: MessageId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie)),
128877
129121
  role: "user",
128878
129122
  text: input.prompt,
128879
129123
  attachments: []
128880
129124
  },
128881
- modelSelection,
129125
+ modelSelection: fusionPair ? settings?.fusionWatcherModelSelection ?? modelSelection : modelSelection,
128882
129126
  titleSeed: input.title,
128883
129127
  runtimeMode,
128884
- interactionMode,
129128
+ interactionMode: fusionPair ? "default" : interactionMode,
128885
129129
  compressMode,
128886
129130
  unpromptedSubagents,
128887
129131
  createdAt
128888
- }, threadId);
129132
+ }, fusionPair?.watcherThreadId ?? threadId);
128889
129133
  return {
128890
129134
  threadId,
128891
129135
  projectId,
@@ -129525,7 +129769,7 @@ const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot
129525
129769
  tool: new McpSchema.Tool({
129526
129770
  name: tool.name,
129527
129771
  description: Tool.getDescription(tool),
129528
- inputSchema: Tool.getJsonSchema(tool),
129772
+ inputSchema: mcpInputSchema(Tool.getJsonSchema(tool)),
129529
129773
  annotations: {
129530
129774
  ...Context.getOption(tool.annotations, Tool.Title).pipe(Option.map((title) => ({ title })), Option.getOrUndefined),
129531
129775
  readOnlyHint: Context.get(tool.annotations, Tool.Readonly),
@@ -129567,13 +129811,13 @@ const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot
129567
129811
  })
129568
129812
  });
129569
129813
  });
129570
- const PreviewStandardToolkitRegistrationLive = McpServer.toolkit(PreviewStandardToolkit).pipe(Layer.provide(PreviewStandardToolkitHandlersLive));
129814
+ const PreviewStandardToolkitRegistrationLive = mcpToolkit(PreviewStandardToolkit).pipe(Layer.provide(PreviewStandardToolkitHandlersLive));
129571
129815
  const PreviewSnapshotRegistrationLive = Layer.effectDiscard(registerPreviewSnapshot()).pipe(Layer.provide(PreviewSnapshotToolkitHandlersLive));
129572
129816
  const PreviewToolkitRegistrationLive = Layer.mergeAll(PreviewStandardToolkitRegistrationLive, PreviewSnapshotRegistrationLive);
129573
- const TaskToolkitRegistrationLive = McpServer.toolkit(TaskToolkit).pipe(Layer.provide(TaskToolkitHandlersLive));
129574
- const ThreadToolkitRegistrationLive = McpServer.toolkit(ThreadToolkit).pipe(Layer.provide(ThreadToolkitHandlersLive));
129575
- const WatchToolkitRegistrationLive = McpServer.toolkit(WatchToolkit).pipe(Layer.provide(WatchToolkitHandlersLive));
129576
- const AdviseToolkitRegistrationLive = McpServer.toolkit(AdviseToolkit).pipe(Layer.provide(AdviseToolkitHandlersLive));
129817
+ const TaskToolkitRegistrationLive = mcpToolkit(TaskToolkit).pipe(Layer.provide(TaskToolkitHandlersLive));
129818
+ const ThreadToolkitRegistrationLive = mcpToolkit(ThreadToolkit).pipe(Layer.provide(ThreadToolkitHandlersLive));
129819
+ const WatchToolkitRegistrationLive = mcpToolkit(WatchToolkit).pipe(Layer.provide(WatchToolkitHandlersLive));
129820
+ const AdviseToolkitRegistrationLive = mcpToolkit(AdviseToolkit).pipe(Layer.provide(AdviseToolkitHandlersLive));
129577
129821
  const McpTransportLive = McpServer.layerHttp({
129578
129822
  name: "P4Code",
129579
129823
  version: version$1,
@@ -129585,7 +129829,7 @@ const ComputerUseMcpLive = ComputerUseToolkitRegistrationLive.pipe(Layer.provide
129585
129829
  version: version$1,
129586
129830
  path: "/mcp/computer-use"
129587
129831
  }).pipe(Layer.provide(McpAuthMiddlewareLive))));
129588
- const layer$3 = Layer.mergeAll(StandardMcpLive, ComputerUseMcpLive);
129832
+ const layer$3 = Layer.mergeAll(Layer.fresh(StandardMcpLive), Layer.fresh(ComputerUseMcpLive));
129589
129833
  //#endregion
129590
129834
  //#region src/hub/HubActivityTransport.ts
129591
129835
  const REQUEST_TIMEOUT_MS = 8e3;