ds-harness-remote 0.4.9 → 0.4.10

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/index.js CHANGED
@@ -4055,6 +4055,10 @@ var HARNESS_API_TRANSFER_CHUNK_BYTES = 512 * 1024;
4055
4055
  var CODEX_APP_TRANSFER_CHUNK_BYTES = 512 * 1024;
4056
4056
  var MAX_HARNESS_API_TRANSFER_BYTES = 288 * 1024 * 1024;
4057
4057
  var MAX_CODEX_APP_TRANSFER_BYTES = 288 * 1024 * 1024;
4058
+ var MAX_DEVICE_NAME_LENGTH = 128;
4059
+ var MAX_DEVICE_PLATFORM_LENGTH = 64;
4060
+ var MAX_DEVICE_VERSION_LENGTH = 64;
4061
+ var MAX_AUTH_TOKEN_LENGTH = 8 * 1024;
4058
4062
  var SECURE_FRAGMENT_MAGIC = new Uint8Array([68, 83, 72, 70]);
4059
4063
  var SECURE_FRAGMENT_VERSION = 1;
4060
4064
  var SECURE_FRAGMENT_HEADER_BYTES = 17;
@@ -4138,6 +4142,73 @@ var messageTypeSchema = external_exports.enum(messageTypes);
4138
4142
  var controlFrameTypeSchema = external_exports.enum(controlFrameTypes);
4139
4143
  var uniqueStrings = (values) => new Set(values).size === values.length;
4140
4144
  var uniqueNumbers = (values) => new Set(values).size === values.length;
4145
+ var utf8Length = (value) => new TextEncoder().encode(value).byteLength;
4146
+ var boundedUtf8Schema = (minimum, maximum) => external_exports.string().refine((value) => utf8Length(value) >= minimum && utf8Length(value) <= maximum);
4147
+ var deviceIdSchema = external_exports.string().refine((value) => /^[0-9A-HJKMNP-TV-Z]{26}$/iu.test(value) || /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value), "Device ID must be a ULID or UUID.");
4148
+ var deviceNameSchema = boundedUtf8Schema(1, MAX_DEVICE_NAME_LENGTH).refine((value) => value.trim().length > 0, "Device name must contain visible text.");
4149
+ var devicePlatformSchema = external_exports.string().min(1).max(MAX_DEVICE_PLATFORM_LENGTH).regex(/^[a-z0-9][a-z0-9._-]*$/u);
4150
+ var identityKeySchema = external_exports.string().regex(/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/u);
4151
+ var deviceVersionSchema = boundedUtf8Schema(1, MAX_DEVICE_VERSION_LENGTH);
4152
+ var authTokenSchema = boundedUtf8Schema(16, MAX_AUTH_TOKEN_LENGTH);
4153
+ var expiresAtSchema = external_exports.number().int().positive().safe();
4154
+ var hostDeviceDescriptorSchema = external_exports.object({
4155
+ deviceId: deviceIdSchema,
4156
+ name: deviceNameSchema,
4157
+ role: external_exports.literal("host"),
4158
+ platform: devicePlatformSchema,
4159
+ identityKey: identityKeySchema,
4160
+ clientVersion: deviceVersionSchema,
4161
+ harnessVersion: deviceVersionSchema.optional()
4162
+ }).strict();
4163
+ var clientDeviceDescriptorSchema = external_exports.object({
4164
+ deviceId: deviceIdSchema,
4165
+ name: deviceNameSchema,
4166
+ role: external_exports.literal("client"),
4167
+ platform: devicePlatformSchema,
4168
+ identityKey: identityKeySchema,
4169
+ clientVersion: deviceVersionSchema
4170
+ }).strict();
4171
+ var accountDeviceDescriptorSchema = external_exports.union([
4172
+ hostDeviceDescriptorSchema,
4173
+ clientDeviceDescriptorSchema
4174
+ ]);
4175
+ var deviceRegistrationRequestSchema = external_exports.object({
4176
+ v: external_exports.literal(PROTOCOL_VERSION),
4177
+ device: accountDeviceDescriptorSchema
4178
+ }).strict();
4179
+ var hostRegistrationCodeRequestSchema = external_exports.object({
4180
+ v: external_exports.literal(PROTOCOL_VERSION),
4181
+ code: external_exports.string().regex(/^[A-Z0-9]{4}-[A-Z0-9]{4}$/u),
4182
+ device: hostDeviceDescriptorSchema
4183
+ }).strict();
4184
+ var deviceRefreshRequestSchema = external_exports.object({
4185
+ deviceId: deviceIdSchema,
4186
+ refreshToken: authTokenSchema
4187
+ }).strict();
4188
+ var deviceTokenPairSchema = external_exports.object({
4189
+ accessToken: authTokenSchema,
4190
+ accessTokenExpiresAt: expiresAtSchema,
4191
+ refreshToken: authTokenSchema,
4192
+ refreshTokenExpiresAt: expiresAtSchema
4193
+ }).strict();
4194
+ var browserAuthorizationExchangeRequestSchema = external_exports.object({
4195
+ v: external_exports.literal(PROTOCOL_VERSION),
4196
+ device: external_exports.object({
4197
+ deviceId: deviceIdSchema,
4198
+ name: deviceNameSchema,
4199
+ role: external_exports.literal("client"),
4200
+ platform: external_exports.literal("browser"),
4201
+ identityKey: identityKeySchema,
4202
+ clientVersion: deviceVersionSchema
4203
+ }).strict()
4204
+ }).strict();
4205
+ var browserAuthorizationExchangeResponseSchema = external_exports.object({
4206
+ accessToken: authTokenSchema,
4207
+ accessTokenExpiresAt: expiresAtSchema,
4208
+ refreshToken: authTokenSchema,
4209
+ refreshTokenExpiresAt: expiresAtSchema,
4210
+ account: boundedUtf8Schema(1, 254).refine((value) => value.trim().length > 0)
4211
+ }).strict();
4141
4212
  var remoteMessageSchema = external_exports.object({
4142
4213
  v: external_exports.literal(PROTOCOL_VERSION),
4143
4214
  id: external_exports.string().min(1),
@@ -14395,6 +14466,25 @@ function hasErrorCode(error, code) {
14395
14466
  return error instanceof Error && "code" in error && error.code === code;
14396
14467
  }
14397
14468
 
14469
+ // src/codex/permissions.ts
14470
+ function codexPermissionPresetFromResponse(value) {
14471
+ const root = record(value);
14472
+ for (const settings of [root, record(root.threadSettings)]) {
14473
+ const sandbox = settings.sandbox ?? settings.sandboxPolicy;
14474
+ const type = record(sandbox).type;
14475
+ if (settings.approvalPolicy === "never" && (sandbox === "danger-full-access" || type === "dangerFullAccess")) {
14476
+ return "danger-full-access";
14477
+ }
14478
+ if (settings.approvalPolicy === "on-request" && (sandbox === "workspace-write" || type === "workspaceWrite")) {
14479
+ return "workspace-write";
14480
+ }
14481
+ }
14482
+ return void 0;
14483
+ }
14484
+ function record(value) {
14485
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
14486
+ }
14487
+
14398
14488
  // src/codex/virtual-harness.ts
14399
14489
  var CODEX_SESSION_PREFIX = "codex:";
14400
14490
  var CODEX_WORKSPACE_PREFIX = "codex-workspace:";
@@ -14643,10 +14733,11 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14643
14733
  return this.selectedModels.get(sessionId) ?? directory?.default ?? modelSelection();
14644
14734
  }
14645
14735
  permissionSelection(sessionId) {
14646
- return this.selectedPermissions.get(sessionId) ?? CODEX_DEFAULT_PERMISSION;
14736
+ return this.selectedPermissions.get(sessionId);
14647
14737
  }
14648
14738
  setPermissionSelection(sessionId, preset) {
14649
- this.selectedPermissions.set(sessionId, preset);
14739
+ if (preset === void 0) this.selectedPermissions.delete(sessionId);
14740
+ else this.selectedPermissions.set(sessionId, preset);
14650
14741
  this.publishProjection(sessionId, "permissions", codexPermissionsProjection(preset), this.nextProjectionSeq());
14651
14742
  }
14652
14743
  commandList(args) {
@@ -14670,17 +14761,22 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14670
14761
  result: { kind: "error", text: "CodeX Remote permission commands do not accept attachments." }
14671
14762
  };
14672
14763
  const requested = (match[1] ?? "").trim();
14673
- if (requested === "") return {
14674
- commandId,
14675
- result: { kind: "success", text: `current preset ${this.permissionSelection(sessionId)}` }
14676
- };
14764
+ if (requested === "") {
14765
+ await this.readHistoryPage(threadId, { maxMessages: 1 }, signal);
14766
+ return {
14767
+ commandId,
14768
+ result: { kind: "success", text: `current preset ${this.permissionSelection(sessionId) ?? "Host settings (not reported)"}` }
14769
+ };
14770
+ }
14677
14771
  if (!isCodexPermissionPreset(requested)) return {
14678
14772
  commandId,
14679
14773
  result: { kind: "error", text: `unknown preset "${requested}" (available: workspace-write, danger-full-access)` }
14680
14774
  };
14681
14775
  const result = await this.client.request("thread/resume", { threadId, permissionPreset: requested }, signal);
14682
- const effective = codexPermissionPresetFromResponse(result) ?? requested;
14776
+ const effective = codexPermissionPresetFromResponse(result);
14777
+ if (effective === void 0) throw new Error("The Host did not confirm the CodeX permission preset.");
14683
14778
  this.setPermissionSelection(sessionId, effective);
14779
+ if (effective !== requested) throw new Error("The Host did not apply the requested CodeX permission preset.");
14684
14780
  return { commandId, result: { kind: "success", text: `preset ${effective}` } };
14685
14781
  }
14686
14782
  sessionTitle(sessionId) {
@@ -14812,7 +14908,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14812
14908
  return queue.iterate(() => this.eventStreams.delete(clientId));
14813
14909
  }
14814
14910
  async sessionFollow(request, signal) {
14815
- const sessionId = sessionIdFromAddress(record(request.address));
14911
+ const sessionId = sessionIdFromAddress(record2(request.address));
14816
14912
  const threadId = nativeThreadId(sessionId);
14817
14913
  const history = await this.readHistoryPage(threadId, {
14818
14914
  maxMessages: optionalPositiveInteger(request.maxMessages)
@@ -14875,7 +14971,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14875
14971
  });
14876
14972
  }
14877
14973
  acceptCodexFrame(follow, frame) {
14878
- const params = record(frame.params);
14974
+ const params = record2(frame.params);
14879
14975
  if (frame.method === "thread/name/updated") {
14880
14976
  const name2 = string(params.threadName);
14881
14977
  if (name2 === void 0) return;
@@ -14887,11 +14983,11 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14887
14983
  }
14888
14984
  if (frame.method === "thread/settings/updated") {
14889
14985
  const preset = codexPermissionPresetFromResponse(params);
14890
- if (preset !== void 0) this.setPermissionSelection(follow.sessionId, preset);
14986
+ this.setPermissionSelection(follow.sessionId, preset);
14891
14987
  return;
14892
14988
  }
14893
14989
  if (frame.method === "turn/started") {
14894
- const turn = record(params.turn);
14990
+ const turn = record2(params.turn);
14895
14991
  this.resetLiveTurn(follow);
14896
14992
  follow.turn += 1;
14897
14993
  follow.activeTurnId = string(turn.id);
@@ -14900,11 +14996,11 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14900
14996
  this.pushEvent(follow, "turn/start", { turn: follow.turn });
14901
14997
  this.pushEvent(follow, "step/start", { turn: follow.turn, step: 1 });
14902
14998
  this.emitRemoteEvent("api-session/status", [follow.sessionId, true]);
14903
- for (const item of array(turn.items)) this.acceptCompletedItem(follow, record(item), false);
14999
+ for (const item of array(turn.items)) this.acceptCompletedItem(follow, record2(item), false);
14904
15000
  return;
14905
15001
  }
14906
15002
  if (frame.method === "item/started" || frame.method === "item/completed") {
14907
- const item = record(params.item);
15003
+ const item = record2(params.item);
14908
15004
  const itemId = string(item.id);
14909
15005
  if (frame.method === "item/started" && itemId !== void 0) {
14910
15006
  follow.startedItems.add(itemId);
@@ -14941,7 +15037,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14941
15037
  }
14942
15038
  if (frame.method === "turn/plan/updated") {
14943
15039
  const todos = array(params.plan).flatMap((value) => {
14944
- const item = record(value);
15040
+ const item = record2(value);
14945
15041
  const content = string(item.step);
14946
15042
  if (content === void 0) return [];
14947
15043
  const status2 = item.status === "inProgress" ? "in_progress" : item.status === "completed" ? "completed" : "pending";
@@ -14975,7 +15071,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
14975
15071
  return;
14976
15072
  }
14977
15073
  if (frame.method === "thread/status/changed") {
14978
- const status2 = record(params.status);
15074
+ const status2 = record2(params.status);
14979
15075
  const running = status2.type === "active";
14980
15076
  this.emitRemoteEvent("api-session/status", [follow.sessionId, running]);
14981
15077
  if (status2.type === "systemError") {
@@ -15002,8 +15098,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15002
15098
  return;
15003
15099
  }
15004
15100
  if (frame.method === "turn/completed") {
15005
- const turn = record(params.turn);
15006
- for (const item of array(turn.items)) this.acceptCompletedItem(follow, record(item), true);
15101
+ const turn = record2(params.turn);
15102
+ for (const item of array(turn.items)) this.acceptCompletedItem(follow, record2(item), true);
15007
15103
  if (follow.stepOpen) {
15008
15104
  this.closeAllStreamBlocks(follow);
15009
15105
  if (follow.streamActive) this.finishStream(follow);
@@ -15098,7 +15194,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15098
15194
  }
15099
15195
  cacheImageBlocks(sessionId, value) {
15100
15196
  for (const block of collectImageBlocks(value)) {
15101
- const attachment = record(block.attachment);
15197
+ const attachment = record2(block.attachment);
15102
15198
  const attachmentId = string(attachment.attachmentId);
15103
15199
  const data = string(block.data);
15104
15200
  if (attachmentId === void 0 || data === void 0 || !attachmentId.startsWith(CODEX_IMAGE_ATTACHMENT_PREFIX)) continue;
@@ -15239,7 +15335,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15239
15335
  }
15240
15336
  async answerRemoteEvent(args, signal) {
15241
15337
  const eventId = string(args.eventId);
15242
- const outcome = record(args.outcome);
15338
+ const outcome = record2(args.outcome);
15243
15339
  if (eventId === void 0) throw new Error("The CodeX approval result is missing its event id.");
15244
15340
  const pending = this.pendingApprovals.get(eventId);
15245
15341
  if (pending === void 0) return void 0;
@@ -15290,7 +15386,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15290
15386
  if (!containsPath(workspace.path, path)) {
15291
15387
  return failure("workspace-not-found", "The selected directory is outside the current CodeX virtual Workspace.");
15292
15388
  }
15293
- const listing = record(await this.client.request("dsh/directoryList", { path }, signal));
15389
+ const listing = record2(await this.client.request("dsh/directoryList", { path }, signal));
15294
15390
  if (!isCodexDirectoryListing(listing)) {
15295
15391
  return failure("internal", "CodeX Remote returned an invalid directory listing.");
15296
15392
  }
@@ -15324,13 +15420,13 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15324
15420
  const directory = await this.models(signal);
15325
15421
  const selection = directory.default;
15326
15422
  const requestedPreset = CODEX_DEFAULT_PERMISSION;
15327
- const result = record(await this.client.request("thread/start", {
15423
+ const result = record2(await this.client.request("thread/start", {
15328
15424
  cwd,
15329
15425
  model: selection.model,
15330
15426
  permissionPreset: requestedPreset
15331
15427
  }, signal));
15332
15428
  const permissionPreset2 = codexPermissionPresetFromResponse(result) ?? requestedPreset;
15333
- const thread = { ...record(result.thread), cwd, turns: array(record(result.thread).turns) };
15429
+ const thread = { ...record2(result.thread), cwd, turns: array(record2(result.thread).turns) };
15334
15430
  const projected = projectCodexThread(thread);
15335
15431
  if (projected === void 0) return failure("internal", "CodeX returned an invalid Thread.");
15336
15432
  this.pendingThreads.set(projected.nativeId, thread);
@@ -15350,16 +15446,14 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15350
15446
  }
15351
15447
  async forkSession(request, signal) {
15352
15448
  const sessionId = requiredString(request.sessionId, "sessionId");
15353
- let permissionPreset2 = this.selectedPermissions.get(sessionId);
15354
- const result = record(await this.client.request("thread/fork", {
15355
- threadId: nativeThreadId(sessionId),
15356
- ...permissionPreset2 === void 0 ? {} : { permissionPreset: permissionPreset2 }
15449
+ const result = record2(await this.client.request("thread/fork", {
15450
+ threadId: nativeThreadId(sessionId)
15357
15451
  }, signal));
15358
- permissionPreset2 = codexPermissionPresetFromResponse(result) ?? permissionPreset2 ?? CODEX_DEFAULT_PERMISSION;
15359
- const projected = projectCodexThread(record(result.thread));
15452
+ const permissionPreset2 = codexPermissionPresetFromResponse(result);
15453
+ const projected = projectCodexThread(record2(result.thread));
15360
15454
  if (projected === void 0) return failure("internal", "CodeX returned an invalid forked Thread.");
15361
15455
  this.selectedModels.set(projected.id, this.modelSelection(sessionId, await this.models(signal)));
15362
- this.selectedPermissions.set(projected.id, permissionPreset2);
15456
+ this.setPermissionSelection(projected.id, permissionPreset2);
15363
15457
  await this.refreshAndPublishWorkspaces();
15364
15458
  const seq = this.nextProjectionSeq();
15365
15459
  this.publishProjection(projected.id, "title", displayTitle(projected), seq);
@@ -15372,7 +15466,6 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15372
15466
  if (input2 === void 0) return failure("attachment-error", "CodeX virtual Sessions accept text and pasted PNG, JPEG, WebP, or GIF images only.");
15373
15467
  const threadId = nativeThreadId(sessionId);
15374
15468
  const selection = this.modelSelection(sessionId, await this.models(signal));
15375
- let permissionPreset2 = this.selectedPermissions.get(sessionId);
15376
15469
  const isFreshBlankThread = this.blankThreads.has(threadId) || this.pendingThreads.has(threadId);
15377
15470
  await this.ensureRcFollow(sessionId);
15378
15471
  if (!isFreshBlankThread) {
@@ -15381,8 +15474,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15381
15474
  model: selection.model
15382
15475
  }, signal);
15383
15476
  const serverPreset = codexPermissionPresetFromResponse(resumeResult);
15384
- if (permissionPreset2 === void 0 && serverPreset !== void 0) {
15385
- permissionPreset2 = serverPreset;
15477
+ if (serverPreset !== void 0) {
15386
15478
  this.setPermissionSelection(sessionId, serverPreset);
15387
15479
  }
15388
15480
  }
@@ -15396,8 +15488,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15396
15488
  await this.client.request(mode, {
15397
15489
  threadId,
15398
15490
  input: input2,
15399
- ...codexModelParams(selection),
15400
- ...permissionPreset2 === void 0 ? {} : { permissionPreset: permissionPreset2 }
15491
+ ...codexModelParams(selection)
15401
15492
  }, signal);
15402
15493
  }
15403
15494
  this.blankThreads.delete(threadId);
@@ -15418,8 +15509,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15418
15509
  }
15419
15510
  async hydrateImageAttachments(sessionId, signal) {
15420
15511
  const threadId = nativeThreadId(sessionId);
15421
- const result = record(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
15422
- const thread = record(result.thread);
15512
+ const result = record2(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
15513
+ const thread = record2(result.thread);
15423
15514
  if (string(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
15424
15515
  this.cacheHistoryImages(projectCodexNativeHistory(thread, sessionId));
15425
15516
  }
@@ -15441,7 +15532,7 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15441
15532
  return success({ title, seq });
15442
15533
  }
15443
15534
  async sessionPage(request, signal) {
15444
- const sessionId = sessionIdFromAddress(record(request.address));
15535
+ const sessionId = sessionIdFromAddress(record2(request.address));
15445
15536
  const page = await this.readHistoryPage(nativeThreadId(sessionId), {
15446
15537
  beforeSeq: optionalNonNegativeInteger(request.beforeSeq),
15447
15538
  throughSeq: optionalInteger(request.throughSeq),
@@ -15454,15 +15545,15 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15454
15545
  return void 0;
15455
15546
  }
15456
15547
  async fetchThread(threadId, signal) {
15457
- const result = record(await this.client.request("thread/read", { threadId, includeTurns: false }, signal));
15458
- const thread = record(result.thread);
15548
+ const result = record2(await this.client.request("thread/read", { threadId, includeTurns: false }, signal));
15549
+ const thread = record2(result.thread);
15459
15550
  if (string(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
15460
15551
  return thread;
15461
15552
  }
15462
15553
  async readHistoryPage(threadId, page, signal) {
15463
15554
  let value;
15464
15555
  try {
15465
- value = record(await this.client.request("dsh/sessionHistory", {
15556
+ value = record2(await this.client.request("dsh/sessionHistory", {
15466
15557
  threadId,
15467
15558
  ...page.beforeSeq === void 0 ? {} : { beforeSeq: page.beforeSeq },
15468
15559
  ...page.throughSeq === void 0 ? {} : { throughSeq: page.throughSeq },
@@ -15470,8 +15561,8 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15470
15561
  }, signal));
15471
15562
  } catch (error) {
15472
15563
  if (!isLegacySessionHistoryUnsupported(error)) throw error;
15473
- const result = record(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
15474
- const thread = record(result.thread);
15564
+ const result = record2(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
15565
+ const thread = record2(result.thread);
15475
15566
  if (string(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
15476
15567
  value = paginateCodexNativeHistory(
15477
15568
  projectCodexNativeHistory(thread, `codex:${threadId}`),
@@ -15485,6 +15576,10 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
15485
15576
  if (!isCodexNativeHistoryPage(value, `codex:${threadId}`)) {
15486
15577
  throw new Error("CodeX Remote returned an invalid paginated History.");
15487
15578
  }
15579
+ if (Object.hasOwn(value, "permissionPreset")) {
15580
+ const preset = value.permissionPreset;
15581
+ this.setPermissionSelection(`codex:${threadId}`, preset === "workspace-write" || preset === "danger-full-access" ? preset : void 0);
15582
+ }
15488
15583
  this.cacheHistoryImages(value);
15489
15584
  return value;
15490
15585
  }
@@ -15692,7 +15787,7 @@ async function loadCatalog(client, signal, pendingThreads) {
15692
15787
  const threads = [];
15693
15788
  let cursor2;
15694
15789
  for (let page = 0; page < MAX_CODEX_PAGES; page += 1) {
15695
- const result = record(await client.request("thread/list", {
15790
+ const result = record2(await client.request("thread/list", {
15696
15791
  limit: CODEX_PAGE_LIMIT,
15697
15792
  sortKey: "updated_at",
15698
15793
  sortDirection: "desc",
@@ -15700,7 +15795,7 @@ async function loadCatalog(client, signal, pendingThreads) {
15700
15795
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
15701
15796
  }, signal));
15702
15797
  for (const value of array(result.data)) {
15703
- const thread = record(value);
15798
+ const thread = record2(value);
15704
15799
  if (string(thread.id) !== void 0) threads.push(thread);
15705
15800
  }
15706
15801
  cursor2 = typeof result.nextCursor === "string" && result.nextCursor.length > 0 ? result.nextCursor : void 0;
@@ -15752,7 +15847,7 @@ async function loadCodexProjects(client, signal) {
15752
15847
  let cursor2;
15753
15848
  try {
15754
15849
  for (let page = 0; page < MAX_CODEX_PAGES; page += 1) {
15755
- const result = record(await client.request("project/list", {
15850
+ const result = record2(await client.request("project/list", {
15756
15851
  limit: CODEX_PAGE_LIMIT,
15757
15852
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
15758
15853
  }, signal));
@@ -15774,18 +15869,18 @@ async function loadModelDirectory(client, signal) {
15774
15869
  let defaultModelId;
15775
15870
  let cursor2;
15776
15871
  for (let page = 0; page < MAX_CODEX_PAGES; page += 1) {
15777
- const result = record(await client.request("model/list", {
15872
+ const result = record2(await client.request("model/list", {
15778
15873
  limit: CODEX_PAGE_LIMIT,
15779
15874
  includeHidden: false,
15780
15875
  ...cursor2 === void 0 ? {} : { cursor: cursor2 }
15781
15876
  }, signal));
15782
15877
  for (const value of array(result.data)) {
15783
- const source = record(value);
15878
+ const source = record2(value);
15784
15879
  if (source.hidden === true) continue;
15785
15880
  const id2 = string(source.model) ?? string(source.id);
15786
15881
  if (id2 === void 0 || models.has(id2)) continue;
15787
15882
  const efforts = array(source.supportedReasoningEfforts).map((value2) => {
15788
- const effort = record(value2);
15883
+ const effort = record2(value2);
15789
15884
  const effortId = string(effort.reasoningEffort);
15790
15885
  if (effortId === void 0) return void 0;
15791
15886
  const description2 = string(effort.description);
@@ -15849,8 +15944,8 @@ function projectCodexNativeHistory(thread, sessionId) {
15849
15944
  return eventSeq;
15850
15945
  };
15851
15946
  for (const rawTurn of array(thread.turns)) {
15852
- const turn = record(rawTurn);
15853
- const turnItems = array(turn.items).map(record);
15947
+ const turn = record2(rawTurn);
15948
+ const turnItems = array(turn.items).map(record2);
15854
15949
  const toolImages = turnItems.flatMap(toolOutputImageBlocks);
15855
15950
  let imageOwner = -1;
15856
15951
  for (let index = 0; index < turnItems.length; index += 1) {
@@ -15999,15 +16094,15 @@ function messageContent(item, attachmentSeed, fallbackType, fallbackText, supple
15999
16094
  }
16000
16095
  function toolOutputImageBlocks(item) {
16001
16096
  const candidates = [
16002
- record(item.result).content,
16003
- record(item.output).content,
16097
+ record2(item.result).content,
16098
+ record2(item.output).content,
16004
16099
  item.contentItems,
16005
16100
  Array.isArray(item.output) ? item.output : void 0
16006
16101
  ];
16007
16102
  for (const candidate of candidates) {
16008
16103
  if (!Array.isArray(candidate)) continue;
16009
16104
  const images = candidate.flatMap((value) => {
16010
- const block = record(value);
16105
+ const block = record2(value);
16011
16106
  return isImageContent2(block) && parseImageBlock(block) !== void 0 ? [block] : [];
16012
16107
  });
16013
16108
  if (images.length > 0) return images;
@@ -16024,7 +16119,7 @@ function contentBlocks(value, attachmentSeed) {
16024
16119
  blocks.push({ type: "text", text: raw });
16025
16120
  continue;
16026
16121
  }
16027
- const block = record(raw);
16122
+ const block = record2(raw);
16028
16123
  const image = imageContentBlock(block, attachmentSeed, imageIndex);
16029
16124
  if (image !== void 0) {
16030
16125
  imageIndex += 1;
@@ -16063,7 +16158,7 @@ function isImageContent2(block) {
16063
16158
  return block.type === "image" || block.type === "input_image";
16064
16159
  }
16065
16160
  function parseImageBlock(block) {
16066
- const url = string(block.url) ?? string(block.image_url) ?? string(record(block.image_url).url);
16161
+ const url = string(block.url) ?? string(block.image_url) ?? string(record2(block.image_url).url);
16067
16162
  const parsed = url === void 0 ? void 0 : parseDataImageUrl2(url);
16068
16163
  if (parsed !== void 0) return parsed;
16069
16164
  const data = string(block.data);
@@ -16220,10 +16315,10 @@ function toolResultEvent(item, turn, step, id2, resultText) {
16220
16315
  };
16221
16316
  }
16222
16317
  function toolResultContentReplacement(event) {
16223
- const data = record(event.data);
16224
- const message = record(data.message);
16318
+ const data = record2(event.data);
16319
+ const message = record2(data.message);
16225
16320
  const content = array(message.content);
16226
- const result = record(content[0]);
16321
+ const result = record2(content[0]);
16227
16322
  if (result.isError === void 0) return event;
16228
16323
  const { isError: _isError, ...stableResult } = result;
16229
16324
  return {
@@ -16263,9 +16358,9 @@ function toolResultText(item, type, text) {
16263
16358
  if (type === "exitedReviewMode") return `Exited review mode${string(item.review) ? `: ${string(item.review)}` : "."}`;
16264
16359
  if (type === "sleep") return `Waited ${integer(item.durationMs) ?? 0} ms.`;
16265
16360
  if (type === "subAgentActivity") {
16266
- return [string(record(item.kind).type) ?? string(item.kind) ?? "Subagent activity", string(item.agentPath)].filter((value) => value !== void 0).join(": ");
16361
+ return [string(record2(item.kind).type) ?? string(item.kind) ?? "Subagent activity", string(item.agentPath)].filter((value) => value !== void 0).join(": ");
16267
16362
  }
16268
- return text ?? itemText2(record(item.result)) ?? itemText2(record(item.output)) ?? string(item.status) ?? type;
16363
+ return text ?? itemText2(record2(item.result)) ?? itemText2(record2(item.output)) ?? string(item.status) ?? type;
16269
16364
  }
16270
16365
  function isSurfaceEvent(type) {
16271
16366
  return type === "user/message" || type === "assistant/message" || type === "tool/result";
@@ -16337,10 +16432,10 @@ function toolResultView(item, type, resultText) {
16337
16432
  };
16338
16433
  }
16339
16434
  function toolDisplayName(item, type) {
16340
- return string(item.name) ?? string(item.tool) ?? string(record(item.tool).name) ?? (type === "mcpToolCall" ? "CodeX MCP tool" : humanizeItemType(type));
16435
+ return string(item.name) ?? string(item.tool) ?? string(record2(item.tool).name) ?? (type === "mcpToolCall" ? "CodeX MCP tool" : humanizeItemType(type));
16341
16436
  }
16342
16437
  function fileLocations(item) {
16343
- return array(item.changes).map((value) => string(record(value).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
16438
+ return array(item.changes).map((value) => string(record2(value).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
16344
16439
  }
16345
16440
  function nativeWorkspace(view) {
16346
16441
  const { sessionCount: _sessionCount, ...workspace } = view;
@@ -16362,10 +16457,10 @@ function projectWorkspace(project) {
16362
16457
  };
16363
16458
  }
16364
16459
  function projectCodexProject(value, fallbackPosition) {
16365
- const source = record(value);
16460
+ const source = record2(value);
16366
16461
  const id2 = string(source.id);
16367
16462
  if (id2 === void 0) return void 0;
16368
- const roots = array(source.roots).map((root) => string(record(root).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
16463
+ const roots = array(source.roots).map((root) => string(record2(root).path)).filter((path) => path !== void 0 && path.length > 0).map((path) => ({ path }));
16369
16464
  if (roots.length === 0) return void 0;
16370
16465
  return {
16371
16466
  id: id2,
@@ -16436,7 +16531,7 @@ function isCanonicalBase642(value) {
16436
16531
  function modelSelectionProjection(selection) {
16437
16532
  return { lastUsed: selection, next: selection };
16438
16533
  }
16439
- function codexPermissionsProjection(currentValue) {
16534
+ function codexPermissionsProjection(preset) {
16440
16535
  return {
16441
16536
  options: [
16442
16537
  {
@@ -16450,7 +16545,7 @@ function codexPermissionsProjection(currentValue) {
16450
16545
  description: "Full Host file access without approval prompts for subsequent turns in this virtual Session."
16451
16546
  }
16452
16547
  ],
16453
- currentValue
16548
+ currentValue: preset ?? "Host settings (not reported)"
16454
16549
  };
16455
16550
  }
16456
16551
  function codexImageLimitsProjection() {
@@ -16466,22 +16561,6 @@ function codexImageLimitsProjection() {
16466
16561
  function isCodexPermissionPreset(value) {
16467
16562
  return value === "workspace-write" || value === "danger-full-access";
16468
16563
  }
16469
- function codexPermissionPresetFromResponse(value) {
16470
- const root = record(value);
16471
- return codexPermissionPresetFromPolicyRecord(root) ?? codexPermissionPresetFromPolicyRecord(record(root.threadSettings));
16472
- }
16473
- function codexPermissionPresetFromPolicyRecord(value) {
16474
- const sandbox = value.sandbox ?? value.sandboxPolicy;
16475
- if (value.approvalPolicy === "never" && isDangerFullAccessSandbox(sandbox)) return "danger-full-access";
16476
- if (value.approvalPolicy === "on-request" && isWorkspaceWriteSandbox(sandbox)) return "workspace-write";
16477
- return void 0;
16478
- }
16479
- function isDangerFullAccessSandbox(value) {
16480
- return value === "danger-full-access" || record(value).type === "dangerFullAccess";
16481
- }
16482
- function isWorkspaceWriteSandbox(value) {
16483
- return value === "workspace-write" || record(value).type === "workspaceWrite";
16484
- }
16485
16564
  function modelCatalog(directory) {
16486
16565
  return {
16487
16566
  default: directory.default,
@@ -16520,7 +16599,7 @@ function itemText2(value) {
16520
16599
  if (content !== void 0) {
16521
16600
  const text = content.map((part) => {
16522
16601
  if (typeof part === "string") return part;
16523
- const block = record(part);
16602
+ const block = record2(part);
16524
16603
  return string(block.text) ?? string(block.content) ?? "";
16525
16604
  }).filter(Boolean).join("\n");
16526
16605
  if (text !== "") return text;
@@ -16534,8 +16613,8 @@ function toolArguments(item) {
16534
16613
  if (item.type === "commandExecution") return { command: commandText(item.command) ?? item.command ?? "" };
16535
16614
  if (item.type === "fileChange") return {
16536
16615
  changes: array(item.changes).map((value) => {
16537
- const change = record(value);
16538
- const kind = typeof change.kind === "string" ? change.kind : string(record(change.kind).type);
16616
+ const change = record2(value);
16617
+ const kind = typeof change.kind === "string" ? change.kind : string(record2(change.kind).type);
16539
16618
  return {
16540
16619
  ...typeof change.path === "string" ? { path: change.path } : {},
16541
16620
  ...kind === void 0 ? {} : { kind }
@@ -16556,7 +16635,7 @@ function toolArguments(item) {
16556
16635
  if (item.type === "subAgentActivity") return {
16557
16636
  agentThreadId: string(item.agentThreadId) ?? "",
16558
16637
  agentPath: string(item.agentPath) ?? "",
16559
- kind: string(record(item.kind).type) ?? string(item.kind) ?? "activity"
16638
+ kind: string(record2(item.kind).type) ?? string(item.kind) ?? "activity"
16560
16639
  };
16561
16640
  if (item.type === "sleep") return { durationMs: integer(item.durationMs) ?? 0 };
16562
16641
  if (item.type === "enteredReviewMode" || item.type === "exitedReviewMode") return {
@@ -16567,19 +16646,19 @@ function toolArguments(item) {
16567
16646
  }
16568
16647
  function fileChangeSummary(item) {
16569
16648
  const changes = array(item.changes).map((value) => {
16570
- const change = record(value);
16649
+ const change = record2(value);
16571
16650
  const path = string(change.path);
16572
- const kind = typeof change.kind === "string" ? change.kind : string(record(change.kind).type);
16651
+ const kind = typeof change.kind === "string" ? change.kind : string(record2(change.kind).type);
16573
16652
  return [kind, path].filter((part) => part !== void 0 && part.length > 0).join(" ");
16574
16653
  }).filter(Boolean);
16575
16654
  return changes.length === 0 ? "File changes completed." : changes.join("\n");
16576
16655
  }
16577
16656
  function sanitizedFileChanges(value) {
16578
16657
  return array(value).flatMap((raw) => {
16579
- const change = record(raw);
16658
+ const change = record2(raw);
16580
16659
  const path = string(change.path);
16581
16660
  if (path === void 0) return [];
16582
- const kind = typeof change.kind === "string" ? change.kind : string(record(change.kind).type);
16661
+ const kind = typeof change.kind === "string" ? change.kind : string(record2(change.kind).type);
16583
16662
  return [{ path, ...kind === void 0 ? {} : { kind } }];
16584
16663
  });
16585
16664
  }
@@ -16609,7 +16688,7 @@ function humanizeItemType(type) {
16609
16688
  function commandText(value) {
16610
16689
  if (typeof value === "string") return value;
16611
16690
  if (Array.isArray(value)) return value.filter((item) => typeof item === "string").join(" ");
16612
- const source = record(value);
16691
+ const source = record2(value);
16613
16692
  return string(source.command) ?? string(source.text);
16614
16693
  }
16615
16694
  function sessionIdFromAddress(address) {
@@ -16625,7 +16704,7 @@ function nativeThreadId(sessionId) {
16625
16704
  function activeTurnId(value) {
16626
16705
  if (!Array.isArray(value)) return void 0;
16627
16706
  for (let index = value.length - 1; index >= 0; index -= 1) {
16628
- const turn = record(value[index]);
16707
+ const turn = record2(value[index]);
16629
16708
  if (isActiveCodexTurn(turn) && typeof turn.id === "string" && turn.id.length > 0) return turn.id;
16630
16709
  }
16631
16710
  return void 0;
@@ -16634,10 +16713,10 @@ function isActiveCodexTurn(turn) {
16634
16713
  return turn.status === "inProgress" || turn.status === "running";
16635
16714
  }
16636
16715
  function carrierArgs(payload) {
16637
- return record(record(payload).args);
16716
+ return record2(record2(payload).args);
16638
16717
  }
16639
16718
  function requestArg(args) {
16640
- return record(args.request ?? args._request ?? args);
16719
+ return record2(args.request ?? args._request ?? args);
16641
16720
  }
16642
16721
  function rcEndpoint(endpoint) {
16643
16722
  if (endpoint === "workspace.list") return "workspace/list";
@@ -16653,20 +16732,20 @@ function ok(value) {
16653
16732
  return value === void 0 ? { ok: true } : { ok: true, value };
16654
16733
  }
16655
16734
  function business(value) {
16656
- const result = record(value);
16735
+ const result = record2(value);
16657
16736
  if (result.ok === true) return ok(result.value);
16658
16737
  if (result.ok === false) {
16659
- const error = record(result.error);
16660
- return fail(string(error.code) ?? "internal", string(error.message) ?? "CodeX virtual Harness rejected the request.", record(error.details));
16738
+ const error = record2(result.error);
16739
+ return fail(string(error.code) ?? "internal", string(error.message) ?? "CodeX virtual Harness rejected the request.", record2(error.details));
16661
16740
  }
16662
16741
  return ok(value);
16663
16742
  }
16664
16743
  function isCodexDirectoryListing(value) {
16665
- const listing = record(value);
16744
+ const listing = record2(value);
16666
16745
  return typeof listing.path === "string" && typeof listing.home === "string" && Array.isArray(listing.crumbs) && listing.crumbs.every(isCodexDirectoryEntry) && Array.isArray(listing.entries) && listing.entries.every(isCodexDirectoryEntry) && typeof listing.truncated === "boolean";
16667
16746
  }
16668
16747
  function isCodexDirectoryEntry(value) {
16669
- const entry = record(value);
16748
+ const entry = record2(value);
16670
16749
  return typeof entry.name === "string" && typeof entry.path === "string" && typeof entry.hidden === "boolean";
16671
16750
  }
16672
16751
  function fail(code, message, details = {}) {
@@ -16686,7 +16765,7 @@ function errorCode(error) {
16686
16765
  function errorDetails(error) {
16687
16766
  return "details" in error && isRecord3(error.details) ? error.details : {};
16688
16767
  }
16689
- function record(value) {
16768
+ function record2(value) {
16690
16769
  return isRecord3(value) ? value : {};
16691
16770
  }
16692
16771
  function isRecord3(value) {
@@ -16721,8 +16800,8 @@ function optionalPositiveInteger(value) {
16721
16800
  return parsed;
16722
16801
  }
16723
16802
  function isCodexNativeHistoryPage(value, sessionId) {
16724
- const page = record(value);
16725
- const header = record(page.header);
16803
+ const page = record2(value);
16804
+ const header = record2(page.header);
16726
16805
  return header.id === sessionId && integer(page.cursor) !== void 0 && integer(page.nextTurn) !== void 0 && Array.isArray(page.records) && typeof page.hasMore === "boolean" && (page.activeTurnId === void 0 || typeof page.activeTurnId === "string");
16727
16806
  }
16728
16807
  function truncateCodePoints(value, maximum) {
@@ -16903,7 +16982,7 @@ function normalizeServerUrl(value) {
16903
16982
  }
16904
16983
 
16905
16984
  // src/version.ts
16906
- var PLUGIN_VERSION = "0.4.9";
16985
+ var PLUGIN_VERSION = "0.4.10";
16907
16986
 
16908
16987
  // src/server-api.ts
16909
16988
  var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
@@ -17218,10 +17297,11 @@ var ServerApiError = class extends Error {
17218
17297
  }
17219
17298
  };
17220
17299
  function validateTokens(value) {
17221
- if (typeof value.accessToken !== "string" || value.accessToken.length < 16 || typeof value.refreshToken !== "string" || value.refreshToken.length < 16 || !Number.isSafeInteger(value.accessTokenExpiresAt) || !Number.isSafeInteger(value.refreshTokenExpiresAt)) {
17300
+ const parsed = deviceTokenPairSchema.safeParse(value);
17301
+ if (!parsed.success) {
17222
17302
  throw new ServerApiError("INVALID_MESSAGE", "The Server returned invalid device credentials.", false);
17223
17303
  }
17224
- return value;
17304
+ return parsed.data;
17225
17305
  }
17226
17306
  function validateWebLogin(value) {
17227
17307
  const item = requireRecord(value, "account login");
@@ -18516,6 +18596,7 @@ function isPrivate(ip) {
18516
18596
  var REMOTE_COMMAND_LIST_MIN_VERSION = [0, 3, 16];
18517
18597
  var REMOTE_FILE_VIEWER_MIN_VERSION = [0, 3, 17];
18518
18598
  var DIRECT_WEBRTC_NEGOTIATE_TIMEOUT_MS = 12e3;
18599
+ var DIRECT_LAN_PROGRESS_DISPLAY_MS = 1400;
18519
18600
  var ClientModeRuntime = class {
18520
18601
  constructor(config, identities, server, apiProxy, typertGateway, logger, host, rtcFactoryProvider = loadNodeRtcFactory) {
18521
18602
  this.config = config;
@@ -18804,12 +18885,12 @@ var ClientModeRuntime = class {
18804
18885
  throw new ClientModeError("FEATURE_NOT_SUPPORTED", "The selected Host does not provide CodeX workspaces.");
18805
18886
  }
18806
18887
  this.assertRemoteCompatible(remote);
18807
- const result = record2(await new CodexRemoteClient(remote.client).request("project/create", {
18888
+ const result = record3(await new CodexRemoteClient(remote.client).request("project/create", {
18808
18889
  name: remoteWorkspaceTitle(trimmedPath),
18809
18890
  roots: [{ path: trimmedPath }],
18810
18891
  idempotencyKey: uuidV7()
18811
18892
  }, signal));
18812
- const project = record2(result.project);
18893
+ const project = record3(result.project);
18813
18894
  if (typeof project.id !== "string" || project.id.length === 0) {
18814
18895
  throw new ClientModeError("INVALID_MESSAGE", "The Host returned an invalid CodeX project.");
18815
18896
  }
@@ -18859,7 +18940,7 @@ var ClientModeRuntime = class {
18859
18940
  }
18860
18941
  async openCodexStream(payload, signal) {
18861
18942
  const remote = this.activeCodexRemote();
18862
- const value = record2(payload);
18943
+ const value = record3(payload);
18863
18944
  if (typeof value.streamId !== "string" || value.streamId.length === 0 || value.streamId.length > 128 || typeof value.threadId !== "string" || value.threadId.length === 0) {
18864
18945
  throw new ClientModeError("INVALID_MESSAGE", "A Codex stream and thread are required.");
18865
18946
  }
@@ -18944,7 +19025,7 @@ var ClientModeRuntime = class {
18944
19025
  };
18945
19026
  }
18946
19027
  async nextCodexFrames(payload, signal) {
18947
- const value = record2(payload);
19028
+ const value = record3(payload);
18948
19029
  if (typeof value.streamId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Codex stream is required.");
18949
19030
  const stream = this.codexStreams.get(value.streamId);
18950
19031
  if (stream === void 0) throw new ClientModeError("STREAM_NOT_FOUND", "The Codex stream is not open.");
@@ -18958,7 +19039,7 @@ var ClientModeRuntime = class {
18958
19039
  };
18959
19040
  }
18960
19041
  async closeCodexStream(payload) {
18961
- const value = record2(payload);
19042
+ const value = record3(payload);
18962
19043
  if (typeof value.streamId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Codex stream is required.");
18963
19044
  const stream = this.codexStreams.get(value.streamId);
18964
19045
  if (stream === void 0) return { closed: false, streamId: value.streamId };
@@ -19069,11 +19150,15 @@ var ClientModeRuntime = class {
19069
19150
  let transport;
19070
19151
  for (const attempt of attempts) {
19071
19152
  webRtcFallback = false;
19072
- this.updateConnectionProgress(progressRunId, "probing", activeTransportsForAttempt(attempt));
19073
- transport = createTransport(attempt);
19074
- client = new RemoteClientCore(new ClientSecureTransport(transport, identity, target), 6e4);
19075
- await client.connect();
19076
- signal?.throwIfAborted();
19153
+ const stopProgressTimer = this.beginAttemptProgress(progressRunId, attempt);
19154
+ try {
19155
+ transport = createTransport(attempt);
19156
+ client = new RemoteClientCore(new ClientSecureTransport(transport, identity, target), 6e4);
19157
+ await client.connect();
19158
+ signal?.throwIfAborted();
19159
+ } finally {
19160
+ stopProgressTimer();
19161
+ }
19077
19162
  if (attempt === "relay" || !webRtcFallback) break;
19078
19163
  await client.close();
19079
19164
  client = void 0;
@@ -19148,6 +19233,19 @@ var ClientModeRuntime = class {
19148
19233
  await previous?.client.close().catch(() => void 0);
19149
19234
  return next;
19150
19235
  }
19236
+ beginAttemptProgress(runId, attempt) {
19237
+ if (attempt !== "direct") {
19238
+ this.updateConnectionProgress(runId, "probing", [attempt]);
19239
+ return () => void 0;
19240
+ }
19241
+ this.updateConnectionProgress(runId, "probing", ["lan"]);
19242
+ const timer = setTimeout(() => {
19243
+ if (this.connectionProgress?.runId === runId && this.connectionProgress.phase === "probing") {
19244
+ this.updateConnectionProgress(runId, "probing", ["p2p"]);
19245
+ }
19246
+ }, DIRECT_LAN_PROGRESS_DISPLAY_MS);
19247
+ return () => clearTimeout(timer);
19248
+ }
19151
19249
  updateConnectionProgress(runId, phase, activeTransports) {
19152
19250
  if (this.connectionProgress?.runId !== runId) return;
19153
19251
  this.connectionProgress = {
@@ -19165,14 +19263,14 @@ var ClientModeRuntime = class {
19165
19263
  if (endpoint === "status") return ok2(await this.detailedStatus());
19166
19264
  if (endpoint === "devices") return ok2(await this.devices());
19167
19265
  if (endpoint === "client.account.login") {
19168
- const value = record2(payload);
19266
+ const value = record3(payload);
19169
19267
  if (typeof value.email !== "string" || typeof value.password !== "string") {
19170
19268
  throw new ClientModeError("INVALID_MESSAGE", "Email and password are required.");
19171
19269
  }
19172
19270
  return ok2(await this.authorizeClientWithAccount(value.email, value.password));
19173
19271
  }
19174
19272
  if (endpoint === "client.account.qr.start") {
19175
- const value = record2(payload);
19273
+ const value = record3(payload);
19176
19274
  const provider = value.provider ?? "zhihu";
19177
19275
  if (provider !== "zhihu" && provider !== "github") {
19178
19276
  throw new ClientModeError("INVALID_MESSAGE", "A supported OAuth provider is required.");
@@ -19180,14 +19278,14 @@ var ClientModeRuntime = class {
19180
19278
  return ok2(await this.startClientOAuthQrLogin(provider));
19181
19279
  }
19182
19280
  if (endpoint === "client.account.qr.poll") {
19183
- const value = record2(payload);
19281
+ const value = record3(payload);
19184
19282
  if (typeof value.qrId !== "string" || value.qrId.length < 20) {
19185
19283
  throw new ClientModeError("INVALID_MESSAGE", "A QR login session is required.");
19186
19284
  }
19187
19285
  return ok2(await this.pollClientOAuthQrLogin(value.qrId));
19188
19286
  }
19189
19287
  if (endpoint === "directory.list") {
19190
- const value = record2(payload);
19288
+ const value = record3(payload);
19191
19289
  if (typeof value.targetDeviceId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Host is required.");
19192
19290
  return ok2(await this.listRemoteDirectory(
19193
19291
  value.targetDeviceId,
@@ -19196,38 +19294,38 @@ var ClientModeRuntime = class {
19196
19294
  ));
19197
19295
  }
19198
19296
  if (endpoint === "workspaces.list") {
19199
- const value = record2(payload);
19297
+ const value = record3(payload);
19200
19298
  if (typeof value.targetDeviceId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Host is required.");
19201
19299
  return ok2(await this.listRemoteWorkspaces(value.targetDeviceId, signal));
19202
19300
  }
19203
19301
  if (endpoint === "codex.workspaces.list") {
19204
- const value = record2(payload);
19302
+ const value = record3(payload);
19205
19303
  if (typeof value.targetDeviceId !== "string") throw new ClientModeError("INVALID_MESSAGE", "A Host is required.");
19206
19304
  return ok2(await this.listCodexWorkspaces(value.targetDeviceId, signal));
19207
19305
  }
19208
19306
  if (endpoint === "workspace.open") {
19209
- const value = record2(payload);
19307
+ const value = record3(payload);
19210
19308
  if (typeof value.targetDeviceId !== "string" || typeof value.path !== "string") {
19211
19309
  throw new ClientModeError("INVALID_MESSAGE", "A Host and working directory are required.");
19212
19310
  }
19213
19311
  return ok2(await this.openRemoteWorkspace(value.targetDeviceId, value.path, signal));
19214
19312
  }
19215
19313
  if (endpoint === "codex.workspace.open") {
19216
- const value = record2(payload);
19314
+ const value = record3(payload);
19217
19315
  if (typeof value.targetDeviceId !== "string" || typeof value.workspaceId !== "string") {
19218
19316
  throw new ClientModeError("INVALID_MESSAGE", "A Host and CodeX Workspace are required.");
19219
19317
  }
19220
19318
  return ok2(await this.openCodexWorkspace(value.targetDeviceId, value.workspaceId, signal));
19221
19319
  }
19222
19320
  if (endpoint === "codex.workspace.create") {
19223
- const value = record2(payload);
19321
+ const value = record3(payload);
19224
19322
  if (typeof value.targetDeviceId !== "string" || typeof value.path !== "string") {
19225
19323
  throw new ClientModeError("INVALID_MESSAGE", "A Host and CodeX project directory are required.");
19226
19324
  }
19227
19325
  return ok2(await this.createCodexWorkspace(value.targetDeviceId, value.path, signal));
19228
19326
  }
19229
19327
  if (endpoint === "workspace.selection.consume") {
19230
- const value = record2(payload);
19328
+ const value = record3(payload);
19231
19329
  if (typeof value.targetDeviceId !== "string" || typeof value.workspaceId !== "string") {
19232
19330
  throw new ClientModeError("INVALID_MESSAGE", "A Host and Workspace are required.");
19233
19331
  }
@@ -19243,7 +19341,7 @@ var ClientModeRuntime = class {
19243
19341
  return ok2(await this.callRemoteFileViewer(method, payload, signal));
19244
19342
  }
19245
19343
  if (endpoint === "codex.call") {
19246
- const value = record2(payload);
19344
+ const value = record3(payload);
19247
19345
  if (typeof value.method !== "string" || !("params" in value)) {
19248
19346
  throw new ClientModeError("INVALID_MESSAGE", "A Codex method and params are required.");
19249
19347
  }
@@ -19267,7 +19365,7 @@ var ClientModeRuntime = class {
19267
19365
  return ok2({ supported: local || remoteSupported, local, remote: remoteSupported });
19268
19366
  }
19269
19367
  if (endpoint === "codex.respond") {
19270
- const value = record2(payload);
19368
+ const value = record3(payload);
19271
19369
  const remote = this.activeCodexRemote();
19272
19370
  if (remote !== void 0) return ok2(await remote.client.rpc("codex.app.respond", value, signal));
19273
19371
  const host = this.requireLocalCodex();
@@ -19278,14 +19376,14 @@ var ClientModeRuntime = class {
19278
19376
  if (endpoint === "codex.stream.close") return ok2(await this.closeCodexStream(payload));
19279
19377
  if (endpoint === "host.account.login") {
19280
19378
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
19281
- const value = record2(payload);
19379
+ const value = record3(payload);
19282
19380
  if (typeof value.email !== "string" || typeof value.password !== "string") {
19283
19381
  throw new ClientModeError("INVALID_MESSAGE", "Email and password are required.");
19284
19382
  }
19285
19383
  return ok2(await this.host.authorizeHostWithAccount(value.email, value.password));
19286
19384
  }
19287
19385
  if (endpoint === "host.authorization.set") {
19288
- const value = record2(payload);
19386
+ const value = record3(payload);
19289
19387
  if (typeof value.enabled !== "boolean") {
19290
19388
  throw new ClientModeError("INVALID_MESSAGE", "Host authorization state is required.");
19291
19389
  }
@@ -19293,14 +19391,14 @@ var ClientModeRuntime = class {
19293
19391
  }
19294
19392
  if (endpoint === "host.registration-code.submit") {
19295
19393
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
19296
- const value = record2(payload);
19394
+ const value = record3(payload);
19297
19395
  if (typeof value.code !== "string" || value.code.trim() === "") {
19298
19396
  throw new ClientModeError("INVALID_MESSAGE", "A Host registration code is required.");
19299
19397
  }
19300
19398
  return ok2(await this.host.authorizeHostWithCode(value.code));
19301
19399
  }
19302
19400
  if (endpoint === "mode.set") {
19303
- const value = record2(payload);
19401
+ const value = record3(payload);
19304
19402
  if (value.mode !== "local" && value.mode !== "remote") throw new ClientModeError("INVALID_MESSAGE", "Mode must be local or remote.");
19305
19403
  return ok2(await this.setMode(value.mode, typeof value.targetDeviceId === "string" ? value.targetDeviceId : void 0, signal));
19306
19404
  }
@@ -19364,7 +19462,7 @@ function webrtcDiagnosticsLogFields(diagnostics) {
19364
19462
  ...diagnostics.selectedPath === void 0 ? {} : { rtcSelectedPath: diagnostics.selectedPath }
19365
19463
  };
19366
19464
  }
19367
- function record2(value) {
19465
+ function record3(value) {
19368
19466
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
19369
19467
  throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
19370
19468
  }
@@ -19528,10 +19626,6 @@ function preferredTransportsForAttempt(attempt) {
19528
19626
  if (attempt === "turn") return ["turn", "relay"];
19529
19627
  return ["relay"];
19530
19628
  }
19531
- function activeTransportsForAttempt(attempt) {
19532
- if (attempt === "direct") return ["lan", "p2p"];
19533
- return [attempt];
19534
- }
19535
19629
  function transportPreferenceForMode(mode) {
19536
19630
  if (mode === "LAN") return "lan";
19537
19631
  if (mode === "P2P") return "p2p";
@@ -19593,24 +19687,24 @@ var IdentityStore = class {
19593
19687
  }
19594
19688
  if (!hasDevice) {
19595
19689
  const keys = generateKeyPair();
19596
- const record5 = { schemaVersion: 1, deviceId: uuidV7(), name: deviceName, publicKey: keys.publicKey };
19597
- await atomicJsonWrite(devicePath, record5, 384);
19690
+ const record6 = { schemaVersion: 1, deviceId: uuidV7(), name: deviceName, publicKey: keys.publicKey };
19691
+ await atomicJsonWrite(devicePath, record6, 384);
19598
19692
  await atomicTextWrite(keyPath, `${keys.privateKey}
19599
19693
  `, 384);
19600
19694
  }
19601
19695
  await assertPrivateMode(keyPath);
19602
19696
  try {
19603
- let record5 = identitySchema.parse(JSON.parse(await readFile(devicePath, "utf8")));
19697
+ let record6 = identitySchema.parse(JSON.parse(await readFile(devicePath, "utf8")));
19604
19698
  const privateKey = (await readFile(keyPath, "utf8")).trim();
19605
19699
  const regenerated = generateKeyPair(fromBase64Url2(privateKey));
19606
- if (regenerated.publicKey !== record5.publicKey) {
19700
+ if (regenerated.publicKey !== record6.publicKey) {
19607
19701
  throw new IdentityInvalidError("device public and private keys do not match");
19608
19702
  }
19609
- if (record5.name !== deviceName) {
19610
- record5 = { ...record5, name: deviceName };
19611
- await atomicJsonWrite(devicePath, record5, 384);
19703
+ if (record6.name !== deviceName) {
19704
+ record6 = { ...record6, name: deviceName };
19705
+ await atomicJsonWrite(devicePath, record6, 384);
19612
19706
  }
19613
- this.identity = { ...record5, privateKey, fingerprint: fingerprint(record5.publicKey) };
19707
+ this.identity = { ...record6, privateKey, fingerprint: fingerprint(record6.publicKey) };
19614
19708
  await this.loadPeers();
19615
19709
  return this.identity;
19616
19710
  } catch (error) {
@@ -19752,10 +19846,10 @@ var ServerCredentialStore = class {
19752
19846
  return parsed.serverUrl === serverUrl && parsed.deviceId === deviceId ? parsed : void 0;
19753
19847
  }
19754
19848
  async save(credentials) {
19755
- const record5 = credentialSchema.parse({ schemaVersion: 1, ...credentials });
19756
- await atomicWrite(this.path, `${JSON.stringify(record5, null, 2)}
19849
+ const record6 = credentialSchema.parse({ schemaVersion: 1, ...credentials });
19850
+ await atomicWrite(this.path, `${JSON.stringify(record6, null, 2)}
19757
19851
  `);
19758
- return record5;
19852
+ return record6;
19759
19853
  }
19760
19854
  async clear() {
19761
19855
  await rm2(this.path, { force: true });
@@ -19822,7 +19916,7 @@ var PluginControlRuntime = class {
19822
19916
  if (endpoint === "devices") return ok3([]);
19823
19917
  if (endpoint === "host.account.login") {
19824
19918
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
19825
- const value = record3(payload);
19919
+ const value = record4(payload);
19826
19920
  if (typeof value.email !== "string" || typeof value.password !== "string") {
19827
19921
  throw new ClientModeError("INVALID_MESSAGE", "Email and password are required.");
19828
19922
  }
@@ -19830,13 +19924,13 @@ var PluginControlRuntime = class {
19830
19924
  }
19831
19925
  if (endpoint === "host.registration-code.submit") {
19832
19926
  if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
19833
- const value = record3(payload);
19927
+ const value = record4(payload);
19834
19928
  if (typeof value.code !== "string" || value.code.trim() === "") {
19835
19929
  throw new ClientModeError("INVALID_MESSAGE", "A Host registration code is required.");
19836
19930
  }
19837
19931
  return ok3(await this.host.authorizeHostWithCode(value.code));
19838
19932
  }
19839
- if (endpoint === "mode.set" && record3(payload).mode === "local") return ok3(this.hostOnlyStatus());
19933
+ if (endpoint === "mode.set" && record4(payload).mode === "local") return ok3(this.hostOnlyStatus());
19840
19934
  throw new ClientModeError("METHOD_NOT_ALLOWED", "Remote Client mode is disabled by the plugin role.");
19841
19935
  } catch (error) {
19842
19936
  return fail3(error);
@@ -19846,7 +19940,7 @@ var PluginControlRuntime = class {
19846
19940
  if (this.settings === void 0) {
19847
19941
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
19848
19942
  }
19849
- const value = record3(payload);
19943
+ const value = record4(payload);
19850
19944
  if (value.role !== "host" && value.role !== "client") {
19851
19945
  throw new ClientModeError("INVALID_MESSAGE", "Role must be Host or Client.");
19852
19946
  }
@@ -19881,7 +19975,7 @@ var PluginControlRuntime = class {
19881
19975
  if (this.settings === void 0) {
19882
19976
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
19883
19977
  }
19884
- const value = record3(payload);
19978
+ const value = record4(payload);
19885
19979
  if (typeof value.serverUrl !== "string") {
19886
19980
  throw new ClientModeError("INVALID_MESSAGE", "Server URL is required.");
19887
19981
  }
@@ -19894,7 +19988,7 @@ var PluginControlRuntime = class {
19894
19988
  if (this.settings === void 0) {
19895
19989
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
19896
19990
  }
19897
- const role = record3(payload).role;
19991
+ const role = record4(payload).role;
19898
19992
  if (role !== "host" && role !== "client") {
19899
19993
  throw new ClientModeError("INVALID_MESSAGE", "Role must be Host or Client.");
19900
19994
  }
@@ -19910,7 +20004,7 @@ var PluginControlRuntime = class {
19910
20004
  if (this.settings === void 0) {
19911
20005
  throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
19912
20006
  }
19913
- const enabled = record3(payload).enabled;
20007
+ const enabled = record4(payload).enabled;
19914
20008
  if (typeof enabled !== "boolean") {
19915
20009
  throw new ClientModeError("INVALID_MESSAGE", "Codex Remote enabled must be a boolean.");
19916
20010
  }
@@ -20019,7 +20113,7 @@ function editableConfig(config) {
20019
20113
  function isRecord6(value) {
20020
20114
  return typeof value === "object" && value !== null && !Array.isArray(value);
20021
20115
  }
20022
- function record3(value) {
20116
+ function record4(value) {
20023
20117
  if (!isRecord6(value)) throw new ClientModeError("INVALID_MESSAGE", "The control request payload is invalid.");
20024
20118
  return value;
20025
20119
  }
@@ -22626,11 +22720,11 @@ function needsDirectoryFallback(result) {
22626
22720
  return code.includes("capability") || code === "directory-picker/unavailable" || code === "directory-picker-unavailable" || message.includes("browse capability") || message.includes("browser capability") || message.includes("brower capability") || message.includes("directory-picker-unavailable");
22627
22721
  }
22628
22722
  function requestArgs(payload) {
22629
- const root = record4(payload);
22723
+ const root = record5(payload);
22630
22724
  const args = isRecord7(root.args) ? root.args : root;
22631
- return record4(args.request ?? args._request ?? args);
22725
+ return record5(args.request ?? args._request ?? args);
22632
22726
  }
22633
- function record4(value) {
22727
+ function record5(value) {
22634
22728
  return isRecord7(value) ? value : {};
22635
22729
  }
22636
22730
  function isRecord7(value) {
@@ -23289,6 +23383,7 @@ var CodexRemoteDomain = class {
23289
23383
  peerDeviceIds = /* @__PURE__ */ new Map();
23290
23384
  turnOwners = /* @__PURE__ */ new Map();
23291
23385
  approvals = /* @__PURE__ */ new Map();
23386
+ permissionPresets = /* @__PURE__ */ new Map();
23292
23387
  approvalExpiryTimer;
23293
23388
  restartTimer;
23294
23389
  restartAttempt = 0;
@@ -23367,6 +23462,7 @@ var CodexRemoteDomain = class {
23367
23462
  if (extractThread(result)?.id === void 0) {
23368
23463
  throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread.");
23369
23464
  }
23465
+ this.rememberPermission(extractThread(result).id, result);
23370
23466
  return result;
23371
23467
  }
23372
23468
  const threadId = threadIdFromParams(call.params);
@@ -23381,12 +23477,23 @@ var CodexRemoteDomain = class {
23381
23477
  }
23382
23478
  );
23383
23479
  const activeTurnId2 = typeof page.activeTurnId === "string" ? page.activeTurnId : this.turnOwners.get(threadId)?.turnId;
23384
- return activeTurnId2 === void 0 ? page : { ...page, activeTurnId: activeTurnId2 };
23480
+ return {
23481
+ ...page,
23482
+ ...activeTurnId2 === void 0 ? {} : { activeTurnId: activeTurnId2 },
23483
+ permissionPreset: this.permissionPresets.get(threadId) ?? null
23484
+ };
23385
23485
  }
23386
23486
  if (call.method === "dsh/directoryList") {
23387
23487
  return this.listCodexDirectory(call.params.path);
23388
23488
  }
23389
23489
  const allowedThread = threadId === void 0 ? void 0 : await this.readKnownThread(threadId);
23490
+ if (call.method === "thread/resume" && call.params.permissionPreset !== void 0) {
23491
+ const owner = this.turnOwners.get(threadId);
23492
+ if (owner !== void 0 && owner.connectionId !== connectionId) {
23493
+ throw new RpcError("CODEX_THREAD_BUSY", "Another Remote client is writing to this CodeX thread.");
23494
+ }
23495
+ return this.changeThreadPermission(threadId, call.params, allowedThread);
23496
+ }
23390
23497
  if (call.method === "thread/read") {
23391
23498
  return this.callUpstream(call.method, call.params);
23392
23499
  }
@@ -23436,8 +23543,18 @@ var CodexRemoteDomain = class {
23436
23543
  if (call.method === "thread/resume" || call.method === "thread/fork" || call.method === "thread/unarchive") {
23437
23544
  await this.assertResultThreadAllowed(result);
23438
23545
  }
23546
+ if (call.method === "thread/resume" || call.method === "thread/fork") {
23547
+ const resultId = extractThread(result)?.id;
23548
+ if (typeof resultId === "string") {
23549
+ this.rememberPermission(resultId, result);
23550
+ if (call.params.permissionPreset !== void 0) await this.publishPermission(resultId, result);
23551
+ }
23552
+ }
23439
23553
  if (call.method === "turn/start" && threadId !== void 0) {
23440
23554
  this.rememberTurnId(threadId, connectionId, extractTurnId(result));
23555
+ if (call.params.permissionPreset !== void 0) {
23556
+ await this.publishPermission(threadId, codexTurnPermissionParams(permission, allowedThread?.cwd));
23557
+ }
23441
23558
  }
23442
23559
  return result;
23443
23560
  } catch (error) {
@@ -23486,6 +23603,7 @@ var CodexRemoteDomain = class {
23486
23603
  this.peers.clear();
23487
23604
  this.peerDeviceIds.clear();
23488
23605
  this.turnOwners.clear();
23606
+ this.permissionPresets.clear();
23489
23607
  if (this.approvalExpiryTimer !== void 0) clearTimeout(this.approvalExpiryTimer);
23490
23608
  this.approvalExpiryTimer = void 0;
23491
23609
  this.approvals.clear();
@@ -23548,6 +23666,7 @@ var CodexRemoteDomain = class {
23548
23666
  this.state = "restarting";
23549
23667
  this.unavailableCode = code;
23550
23668
  this.turnOwners.clear();
23669
+ this.permissionPresets.clear();
23551
23670
  this.approvals.clear();
23552
23671
  if (this.approvalExpiryTimer !== void 0) clearTimeout(this.approvalExpiryTimer);
23553
23672
  this.approvalExpiryTimer = void 0;
@@ -23603,6 +23722,10 @@ var CodexRemoteDomain = class {
23603
23722
  return;
23604
23723
  }
23605
23724
  const threadId = extractThreadId(message.params);
23725
+ if (message.method === "thread/settings/updated" && threadId !== void 0) {
23726
+ this.rememberPermission(threadId, message.params);
23727
+ }
23728
+ if (message.method === "thread/closed" && threadId !== void 0) this.permissionPresets.delete(threadId);
23606
23729
  if (message.method === "turn/completed" && threadId !== void 0) this.turnOwners.delete(threadId);
23607
23730
  if (message.method === "turn/started" && threadId !== void 0) {
23608
23731
  const owner = this.turnOwners.get(threadId);
@@ -23618,6 +23741,42 @@ var CodexRemoteDomain = class {
23618
23741
  params: message.params
23619
23742
  })));
23620
23743
  }
23744
+ rememberPermission(threadId, value) {
23745
+ const preset = codexPermissionPresetFromResponse(value);
23746
+ if (preset === void 0) this.permissionPresets.delete(threadId);
23747
+ else this.permissionPresets.set(threadId, preset);
23748
+ }
23749
+ async changeThreadPermission(threadId, params, allowed) {
23750
+ const permission = codexPermission(params);
23751
+ const settings = {
23752
+ ...codexTurnPermissionParams(permission, allowed.cwd),
23753
+ ...params.model === void 0 ? {} : { model: params.model }
23754
+ };
23755
+ let result;
23756
+ try {
23757
+ await this.callUpstream("thread/settings/update", { threadId, ...settings });
23758
+ result = { thread: allowed.thread, ...settings, sandbox: settings.sandboxPolicy };
23759
+ } catch {
23760
+ result = await this.callUpstream("thread/resume", {
23761
+ ...permission.params,
23762
+ ...allowed.cwd === void 0 ? {} : { cwd: allowed.cwd },
23763
+ ...codexThreadPermissionParams(permission),
23764
+ excludeTurns: true
23765
+ });
23766
+ await this.assertResultThreadAllowed(result);
23767
+ if (codexPermissionPresetFromResponse(result) !== params.permissionPreset) {
23768
+ await this.callUpstream("thread/settings/update", { threadId, ...settings });
23769
+ result = { ...isRecord10(result) ? result : {}, ...settings, sandbox: settings.sandboxPolicy };
23770
+ }
23771
+ }
23772
+ await this.publishPermission(threadId, result);
23773
+ return result;
23774
+ }
23775
+ async publishPermission(threadId, value) {
23776
+ const source = isRecord10(value) ? value : {};
23777
+ const threadSettings = { approvalPolicy: source.approvalPolicy, sandboxPolicy: source.sandboxPolicy ?? source.sandbox };
23778
+ await this.handleInbound({ kind: "notification", method: "thread/settings/updated", params: { threadId, threadSettings } });
23779
+ }
23621
23780
  async handleServerRequest(message) {
23622
23781
  const appServer = this.requireAppServer();
23623
23782
  await this.expireApprovals();
@@ -24078,7 +24237,7 @@ function filterThreadListByWorkspaceAuthority(result, authority) {
24078
24237
  }
24079
24238
  return {
24080
24239
  ...result,
24081
- data: result.data.map((record5) => isRecord10(record5) ? record5 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
24240
+ data: result.data.map((record6) => isRecord10(record6) ? record6 : void 0).filter((thread) => thread !== void 0 && isThreadAllowedByWorkspaceAuthority(thread, authority))
24082
24241
  };
24083
24242
  }
24084
24243
  function sanitizeProject(value) {