@schoolai/shipyard 3.29.0 → 3.30.0-rc.20260912.0

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.
Files changed (34) hide show
  1. package/dist/capability-detector-worker.js +5 -5
  2. package/dist/{chunk-CDOIPKD6.js → chunk-D7AICUJM.js} +2 -2
  3. package/dist/{chunk-HIGCNT53.js → chunk-FARMJBC7.js} +3 -3
  4. package/dist/chunk-FARMJBC7.js.map +1 -0
  5. package/dist/{chunk-IJGA4YA3.js → chunk-J2QE7DS3.js} +2 -2
  6. package/dist/{chunk-3NS2YSRP.js → chunk-JVE24NQT.js} +35 -8
  7. package/dist/{chunk-3NS2YSRP.js.map → chunk-JVE24NQT.js.map} +1 -1
  8. package/dist/{chunk-ASK7AIJM.js → chunk-QV5KC6S4.js} +11 -3
  9. package/dist/{chunk-ASK7AIJM.js.map → chunk-QV5KC6S4.js.map} +1 -1
  10. package/dist/{chunk-SR3T3EYE.js → chunk-RGVTFZDF.js} +3 -3
  11. package/dist/{chunk-SMGVXANE.js → chunk-SGFCXJJJ.js} +2 -2
  12. package/dist/{chunk-J5MLXWO5.js → chunk-WO5EM3BP.js} +2 -2
  13. package/dist/{chunk-CB35TLO4.js → chunk-ZDJEJI62.js} +2 -2
  14. package/dist/cursor-runner.js +3 -3
  15. package/dist/electron-utility.js +2 -2
  16. package/dist/index.js +4 -4
  17. package/dist/{login-D23INMXJ.js → login-6YTDDUP4.js} +3 -3
  18. package/dist/{plan-backfill-7OALHIQK.js → plan-backfill-5THORN4G.js} +4 -4
  19. package/dist/{serve-VBMV5LEN.js → serve-RFNU5ZMH.js} +261 -74
  20. package/dist/{serve-VBMV5LEN.js.map → serve-RFNU5ZMH.js.map} +1 -1
  21. package/dist/{skills-FSYFQ6FY.js → skills-BX2NZKPN.js} +3 -3
  22. package/dist/{start-NNW7R4M2.js → start-CTCJAB5U.js} +6 -6
  23. package/package.json +1 -1
  24. package/dist/chunk-HIGCNT53.js.map +0 -1
  25. /package/dist/{chunk-CDOIPKD6.js.map → chunk-D7AICUJM.js.map} +0 -0
  26. /package/dist/{chunk-IJGA4YA3.js.map → chunk-J2QE7DS3.js.map} +0 -0
  27. /package/dist/{chunk-SR3T3EYE.js.map → chunk-RGVTFZDF.js.map} +0 -0
  28. /package/dist/{chunk-SMGVXANE.js.map → chunk-SGFCXJJJ.js.map} +0 -0
  29. /package/dist/{chunk-J5MLXWO5.js.map → chunk-WO5EM3BP.js.map} +0 -0
  30. /package/dist/{chunk-CB35TLO4.js.map → chunk-ZDJEJI62.js.map} +0 -0
  31. /package/dist/{login-D23INMXJ.js.map → login-6YTDDUP4.js.map} +0 -0
  32. /package/dist/{plan-backfill-7OALHIQK.js.map → plan-backfill-5THORN4G.js.map} +0 -0
  33. /package/dist/{skills-FSYFQ6FY.js.map → skills-BX2NZKPN.js.map} +0 -0
  34. /package/dist/{start-NNW7R4M2.js.map → start-CTCJAB5U.js.map} +0 -0
@@ -12,7 +12,7 @@ import {
12
12
  guardedSubscribe,
13
13
  makeInitialAttemptState,
14
14
  shutdownFileWatcherGuard
15
- } from "./chunk-J5MLXWO5.js";
15
+ } from "./chunk-WO5EM3BP.js";
16
16
  import {
17
17
  TRAILER_SCHEMA_VERSION,
18
18
  appendTrailerToMessage,
@@ -40,13 +40,13 @@ import {
40
40
  startPlanBackfillForDaemon,
41
41
  sync,
42
42
  validatePeerId
43
- } from "./chunk-CDOIPKD6.js";
43
+ } from "./chunk-D7AICUJM.js";
44
44
  import {
45
45
  loadAuthToken
46
46
  } from "./chunk-XPYW5WQS.js";
47
47
  import {
48
48
  getDaemonVersion
49
- } from "./chunk-SR3T3EYE.js";
49
+ } from "./chunk-RGVTFZDF.js";
50
50
  import {
51
51
  collectPriorCrashDetail,
52
52
  getRecentWasmPanicMessages
@@ -100,6 +100,7 @@ import {
100
100
  detectCodexAuth,
101
101
  detectDevinAuth,
102
102
  detectMarketplacePlugins,
103
+ devinAcpModeId,
103
104
  devinProfile,
104
105
  emitStartupComplete,
105
106
  ensureLabel,
@@ -187,12 +188,12 @@ import {
187
188
  unresolveReviewThread,
188
189
  withLabelAsync,
189
190
  writeCursorApiKeyToVault
190
- } from "./chunk-3NS2YSRP.js";
191
+ } from "./chunk-JVE24NQT.js";
191
192
  import {
192
193
  classifySkill,
193
194
  detectSkills,
194
195
  resolveDevinConfigHome
195
- } from "./chunk-CB35TLO4.js";
196
+ } from "./chunk-ZDJEJI62.js";
196
197
  import {
197
198
  buildNodeSpawnEnv,
198
199
  nodeSpawnEnvOverlay,
@@ -253,7 +254,7 @@ import {
253
254
  createAssetResolver,
254
255
  toSdkContent,
255
256
  toSdkPermissionMode
256
- } from "./chunk-IJGA4YA3.js";
257
+ } from "./chunk-J2QE7DS3.js";
257
258
  import {
258
259
  AGENT_IDS,
259
260
  AGENT_SYSTEM_CLAUDE_CODE,
@@ -462,7 +463,7 @@ import {
462
463
  unsafeAssertSourceId,
463
464
  vizFileExtension,
464
465
  withPreferredRuntimeAuthMethod
465
- } from "./chunk-ASK7AIJM.js";
466
+ } from "./chunk-QV5KC6S4.js";
466
467
  import {
467
468
  AuthGitHubCallbackRequestSchema,
468
469
  AuthGitHubCallbackResponseSchema,
@@ -510,7 +511,7 @@ import {
510
511
  VaultKeyPutResponseSchema,
511
512
  classifyClaudeCodeCompatibility,
512
513
  classifyCodexCompatibility
513
- } from "./chunk-HIGCNT53.js";
514
+ } from "./chunk-FARMJBC7.js";
514
515
  import {
515
516
  external_exports as external_exports2
516
517
  } from "./chunk-EHQITHQX.js";
@@ -3969,6 +3970,7 @@ function buildBroadcastSnapshot(caps) {
3969
3970
  * the slot so both paths agree.
3970
3971
  */
3971
3972
  cursorAuth: caps.cursorAuth ?? null,
3973
+ devinAuth: caps.devinAuth ?? null,
3972
3974
  browserMetricsIntervalMs: null,
3973
3975
  installedRuntimes: caps.installedRuntimes,
3974
3976
  detectionComplete: caps.detectionComplete,
@@ -4011,6 +4013,22 @@ function mergeEnvironmentPatch(current, patch) {
4011
4013
  for (const env of patch) byPath.set(env.path, env);
4012
4014
  return [...byPath.values()];
4013
4015
  }
4016
+ function preserveFullSnapshotState(fresh, current, reconciledSkills, devinAuthAtStart) {
4017
+ const effectiveDevinAuth = current?.devinAuth !== devinAuthAtStart ? current?.devinAuth : fresh.devinAuth ?? current?.devinAuth ?? null;
4018
+ const snapshot = {
4019
+ ...fresh,
4020
+ ...reconciledSkills !== void 0 ? { skills: reconciledSkills } : {},
4021
+ cursorAuth: fresh.cursorAuth ?? current?.cursorAuth ?? null,
4022
+ /** A timed-out native status probe is inconclusive; keep the last authoritative Devin result. */
4023
+ devinAuth: effectiveDevinAuth,
4024
+ depsStatus: fresh.depsStatus ?? current?.depsStatus,
4025
+ unscopedWorkspace: fresh.unscopedWorkspace ?? current?.unscopedWorkspace
4026
+ };
4027
+ return {
4028
+ ...snapshot,
4029
+ runtimeAuth: buildRuntimeAuthMapFromSnapshot(snapshotProfileAuth(snapshot))
4030
+ };
4031
+ }
4014
4032
  function createCapabilitySnapshotBroadcaster(deps) {
4015
4033
  let lastBroadcastSerialized = null;
4016
4034
  let lastSkillsBroadcastSerialized = null;
@@ -4092,28 +4110,19 @@ function createCapabilitySnapshotBroadcaster(deps) {
4092
4110
  broadcastCapabilitiesSnapshot();
4093
4111
  if (effectivePatch.skills !== void 0) broadcastSkillsSnapshot();
4094
4112
  };
4095
- const applyFullSnapshot = async (fresh) => {
4113
+ const applyFullSnapshot = async (fresh, guard) => {
4096
4114
  if (deps.isDisposed()) return;
4115
+ const devinAuthAtStart = guard ? guard.devinAuthBaseline : deps.getCapabilities()?.devinAuth;
4097
4116
  const reconciledSkills = await reconcileSkillsIfNeeded(fresh.skills);
4098
4117
  if (deps.isDisposed()) return;
4099
4118
  const disabled = await loadDisabledMcpServers();
4100
4119
  if (deps.isDisposed()) return;
4101
- const current = deps.getCapabilities();
4102
- const preserved = {
4103
- ...fresh,
4104
- ...reconciledSkills !== void 0 ? { skills: reconciledSkills } : {},
4105
- cursorAuth: fresh.cursorAuth ?? current?.cursorAuth ?? null,
4106
- runtimeAuth: current?.runtimeAuth?.cursor !== void 0 ? { ...fresh.runtimeAuth, cursor: current.runtimeAuth.cursor } : fresh.runtimeAuth,
4107
- /**
4108
- * Same preservation reason as cursorAuth: the full-detect worker does
4109
- * not produce depsStatus/unscopedWorkspace (they are seeded at boot and
4110
- * recomputed only in the cwd-scoped patch), so a full refresh would
4111
- * otherwise wipe them and silently drop the editor's deps/no-project
4112
- * banner ~30s after boot. Preserve last-known when fresh lacks them.
4113
- */
4114
- depsStatus: fresh.depsStatus ?? current?.depsStatus,
4115
- unscopedWorkspace: fresh.unscopedWorkspace ?? current?.unscopedWorkspace
4116
- };
4120
+ const preserved = preserveFullSnapshotState(
4121
+ fresh,
4122
+ deps.getCapabilities(),
4123
+ reconciledSkills,
4124
+ devinAuthAtStart
4125
+ );
4117
4126
  const next = normalizeCapabilities(preserved);
4118
4127
  const nextMcpServers = resolveFilteredMcpServers(next.mcpServers, disabled);
4119
4128
  deps.setCapabilities({
@@ -6049,7 +6058,7 @@ function nanoid(size = 21) {
6049
6058
  }
6050
6059
 
6051
6060
  // src/services/bootstrap/signaling.ts
6052
- var DAEMON_NPM_VERSION = true ? "3.29.0" : "unknown";
6061
+ var DAEMON_NPM_VERSION = true ? "3.30.0" : "unknown";
6053
6062
  function createDaemonSignaling(config) {
6054
6063
  const agentId = config.agentId ?? nanoid();
6055
6064
  function send(msg) {
@@ -14978,6 +14987,7 @@ function filterOutboundForCollab(msg, collabTaskId, getTaskSync) {
14978
14987
  /** Suppressed: host-only settings and schedules */
14979
14988
  case "user_settings_snapshot":
14980
14989
  case "user_settings_updated":
14990
+ case "user_setting_update_ack":
14981
14991
  /**
14982
14992
  * Project structure is currently host-only. When collab wiring lands,
14983
14993
  * shared projects become the share boundary and these will flip to
@@ -16373,7 +16383,7 @@ function routeMessage(msg, callbacks, sendControl, log) {
16373
16383
  callbacks.onRequestInboxSnapshot();
16374
16384
  break;
16375
16385
  case "update_user_setting":
16376
- callbacks.onUpdateUserSetting(msg.key, msg.value);
16386
+ callbacks.onUpdateUserSetting(msg.key, msg.value, msg.requestId);
16377
16387
  break;
16378
16388
  case "request_user_settings":
16379
16389
  callbacks.onRequestUserSettings();
@@ -17913,7 +17923,7 @@ async function refreshPluginCapabilities(daemon, tokenStore) {
17913
17923
  readPreferredClaudeAuthMethod(userSettings)
17914
17924
  )
17915
17925
  ),
17916
- import("./skills-FSYFQ6FY.js").then(
17926
+ import("./skills-BX2NZKPN.js").then(
17917
17927
  (m2) => m2.detectSkills(daemon.capabilities.environments)
17918
17928
  )
17919
17929
  ]);
@@ -26724,13 +26734,28 @@ function buildTaskCallbacks(deps) {
26724
26734
  function buildUserSettingsCallbacks(deps) {
26725
26735
  const { daemon, getControlHandler, logAdapter } = deps;
26726
26736
  return {
26727
- onUpdateUserSetting: (key, value) => {
26728
- daemon.userSettingsStore.updateSetting(key, value).catch((err3) => {
26737
+ onUpdateUserSetting: (key, value, requestId) => {
26738
+ daemon.userSettingsStore.updateSetting(key, value).then(() => {
26739
+ if (requestId) {
26740
+ getControlHandler().sendControl({
26741
+ type: "user_setting_update_ack",
26742
+ requestId,
26743
+ ok: true
26744
+ });
26745
+ }
26746
+ }).catch((err3) => {
26729
26747
  logAdapter({
26730
26748
  event: "user_setting_update_failed",
26731
26749
  key,
26732
26750
  error: err3 instanceof Error ? err3.message : String(err3)
26733
26751
  });
26752
+ if (requestId) {
26753
+ getControlHandler().sendControl({
26754
+ type: "user_setting_update_ack",
26755
+ requestId,
26756
+ ok: false
26757
+ });
26758
+ }
26734
26759
  getControlHandler().sendControl({
26735
26760
  type: "error",
26736
26761
  error: `Failed to update setting "${key}": ${err3 instanceof Error ? err3.message : String(err3)}`
@@ -35246,6 +35271,9 @@ function applyPersistedComposerSettings(orchestrator, settings) {
35246
35271
  patch.compactionPrefs = normalizedSettings.compactionPrefs;
35247
35272
  if (normalizedSettings.mutedResources != null)
35248
35273
  patch.mutedResources = normalizedSettings.mutedResources;
35274
+ if (normalizedSettings.permissionMode != null) {
35275
+ orchestrator.seedConfirmedPermissionMode(normalizedSettings.permissionMode);
35276
+ }
35249
35277
  if (Object.keys(patch).length > 0) orchestrator.applySettings(patch);
35250
35278
  }
35251
35279
  function persistMessageBorneComposerSettings(deps, settings) {
@@ -63877,6 +63905,8 @@ var AutomationService = class {
63877
63905
  case "init_received":
63878
63906
  case "available_commands_updated":
63879
63907
  case "mcp_mid_thread_reload_needed":
63908
+ case "permission_mode_applied":
63909
+ case "permission_mode_rejected":
63880
63910
  case "auth_not_logged_in":
63881
63911
  case "rate_limit":
63882
63912
  case "billing_error":
@@ -72967,6 +72997,9 @@ function createDeferredSubprocess(initialize, onEvent, options = {}) {
72967
72997
  });
72968
72998
  }
72969
72999
  return {
73000
+ reconcilePermissionModeBeforePrompt: options.reconcilePermissionModeBeforePrompt,
73001
+ preservePendingPermissionRequestsInBypass: options.preservePendingPermissionRequestsInBypass,
73002
+ permissionModeRequiresNativeAck: options.permissionModeRequiresNativeAck,
72970
73003
  get isClosed() {
72971
73004
  return controller.signal.aborted || inner?.isClosed === true;
72972
73005
  },
@@ -73327,8 +73360,7 @@ async function prepareDevinRuntimeConfig(options) {
73327
73360
  const configPath = path5.join(devinDir, "config.json");
73328
73361
  const config = {
73329
73362
  auto_update: false,
73330
- read_config_from: READ_CONFIG_FROM_NONE,
73331
- permissions: { ask: ["Write(/**)", "exec", "fetch", "mcp__*"] }
73363
+ read_config_from: READ_CONFIG_FROM_NONE
73332
73364
  };
73333
73365
  const mcpConfig = { mcpServers: buildDevinMcpServers(options.servers) };
73334
73366
  const ruleChunks = instructions ? splitUtf8(instructions, DEVIN_LOADED_RULE_MAX_BYTES) : [];
@@ -77557,6 +77589,9 @@ var ClientSideConnection = class {
77557
77589
  };
77558
77590
 
77559
77591
  // src/services/session/devin-acp-session.ts
77592
+ var DevinPermissionModeRejectedError = class extends Error {
77593
+ name = "DevinPermissionModeRejectedError";
77594
+ };
77560
77595
  var DevinAcpSession = class {
77561
77596
  #connection;
77562
77597
  #options;
@@ -77670,10 +77705,18 @@ var DevinAcpSession = class {
77670
77705
  async setMode(modeId) {
77671
77706
  if (!this.#sessionId) throw new Error("Devin session has not started");
77672
77707
  if (!this.#modes?.availableModes.some((mode) => mode.id === modeId)) {
77673
- throw new Error(`Devin CLI does not advertise permission mode ${modeId}`);
77708
+ throw new DevinPermissionModeRejectedError(
77709
+ `Devin CLI does not advertise permission mode ${modeId}`
77710
+ );
77674
77711
  }
77675
77712
  if (this.#modes.currentModeId !== modeId) {
77676
- await this.#connection.setSessionMode({ sessionId: this.#sessionId, modeId });
77713
+ try {
77714
+ await this.#connection.setSessionMode({ sessionId: this.#sessionId, modeId });
77715
+ } catch (error) {
77716
+ throw new DevinPermissionModeRejectedError(
77717
+ error instanceof Error ? error.message : String(error)
77718
+ );
77719
+ }
77677
77720
  this.#modes = { ...this.#modes, currentModeId: modeId };
77678
77721
  }
77679
77722
  }
@@ -78114,19 +78157,6 @@ function patchCognitionToolMetadata(existing, meta) {
78114
78157
  const planFilePath = typeof pathPatch === "string" || pathPatch === null ? typeof pathPatch === "string" ? pathPatch : void 0 : existing?.planFilePath;
78115
78158
  return { isExitPlan, planFilePath };
78116
78159
  }
78117
- function acpMode(mode) {
78118
- switch (mode) {
78119
- case "plan":
78120
- return "plan";
78121
- case "bypass":
78122
- return "bypass";
78123
- case "auto":
78124
- return "smart";
78125
- case "default":
78126
- case "accept-edits":
78127
- return "accept-edits";
78128
- }
78129
- }
78130
78160
  function mergeToolPatch(existing, toolCallId, patch) {
78131
78161
  const title = patchNullableString(existing?.title, patch.title);
78132
78162
  const explicitToolName = patchExplicitToolName(existing?.explicitToolName, patch);
@@ -78187,6 +78217,7 @@ var DevinAgentSubprocess = class {
78187
78217
  #options;
78188
78218
  #child;
78189
78219
  #session;
78220
+ #startup;
78190
78221
  #translator = new DevinEventTranslator();
78191
78222
  #initialServerConfigs;
78192
78223
  #closed = false;
@@ -78200,6 +78231,8 @@ var DevinAgentSubprocess = class {
78200
78231
  #sessionId = null;
78201
78232
  #tools = /* @__PURE__ */ new Map();
78202
78233
  reconcilePermissionModeBeforePrompt = true;
78234
+ preservePendingPermissionRequestsInBypass = true;
78235
+ permissionModeRequiresNativeAck = true;
78203
78236
  #executingTools = /* @__PURE__ */ new Set();
78204
78237
  #compaction = null;
78205
78238
  #context = null;
@@ -78230,7 +78263,7 @@ var DevinAgentSubprocess = class {
78230
78263
  mcpServers: options.mcpServers ?? [],
78231
78264
  resumeSessionId: options.resumeSessionId,
78232
78265
  forkMessageId: options.forkMessageId,
78233
- modeId: acpMode(options.permissionMode),
78266
+ modeId: devinAcpModeId(options.permissionMode),
78234
78267
  modelId: this.#nativeModel,
78235
78268
  onQuestion: options.onQuestion,
78236
78269
  onExtensionNotification: (method, params) => this.#handleExtensionNotification(method, params),
@@ -78316,7 +78349,7 @@ var DevinAgentSubprocess = class {
78316
78349
  if (options.initialContent.length > 0) {
78317
78350
  this.#pushMessage(options.initialContent, options.initialMessageKind === "synthetic");
78318
78351
  }
78319
- void this.#start();
78352
+ this.#startup = this.#start();
78320
78353
  }
78321
78354
  get isClosed() {
78322
78355
  return this.#exited;
@@ -78385,7 +78418,8 @@ var DevinAgentSubprocess = class {
78385
78418
  } finally {
78386
78419
  if (timer) clearTimeout(timer);
78387
78420
  }
78388
- if (this.#closed) return;
78421
+ if (this.#closed) return false;
78422
+ this.#emit({ type: "permission_mode_applied", mode: this.#options.permissionMode });
78389
78423
  this.#emit({
78390
78424
  type: "init_received",
78391
78425
  sessionId: this.#sessionId,
@@ -78398,7 +78432,15 @@ var DevinAgentSubprocess = class {
78398
78432
  agentSystem: "devin"
78399
78433
  }
78400
78434
  });
78435
+ return true;
78401
78436
  } catch (error) {
78437
+ if (error instanceof DevinPermissionModeRejectedError) {
78438
+ this.#emit({
78439
+ type: "permission_mode_rejected",
78440
+ mode: this.#options.permissionMode,
78441
+ error: error.message
78442
+ });
78443
+ }
78402
78444
  if (!this.#errorReported) {
78403
78445
  this.#emit({
78404
78446
  type: "sdk_error",
@@ -78406,6 +78448,7 @@ var DevinAgentSubprocess = class {
78406
78448
  });
78407
78449
  }
78408
78450
  this.close();
78451
+ return false;
78409
78452
  }
78410
78453
  }
78411
78454
  #emit(event) {
@@ -78583,7 +78626,8 @@ var DevinAgentSubprocess = class {
78583
78626
  return operation;
78584
78627
  }
78585
78628
  async setPermissionMode(mode) {
78586
- await this.#session.setMode(acpMode(mode));
78629
+ if (!await this.#startup) throw new Error("Devin session did not start");
78630
+ await this.#session.setMode(devinAcpModeId(mode));
78587
78631
  }
78588
78632
  async setMcpServers(servers) {
78589
78633
  const next = new Map(
@@ -78910,7 +78954,12 @@ function buildDevinTaskSubprocess(options) {
78910
78954
  (event) => {
78911
78955
  if (isLiveSubprocessEpoch(options.taskId, epoch)) options.onEvent(event);
78912
78956
  },
78913
- { atomicModelSelection: true }
78957
+ {
78958
+ atomicModelSelection: true,
78959
+ reconcilePermissionModeBeforePrompt: true,
78960
+ preservePendingPermissionRequestsInBypass: true,
78961
+ permissionModeRequiresNativeAck: true
78962
+ }
78914
78963
  );
78915
78964
  }
78916
78965
 
@@ -87246,6 +87295,7 @@ function buildUserSettingsStore(filePath) {
87246
87295
  }
87247
87296
  return {
87248
87297
  async getSettings() {
87298
+ await writeQueue;
87249
87299
  return readStore();
87250
87300
  },
87251
87301
  async updateSetting(key, value) {
@@ -91798,24 +91848,29 @@ var PermissionHandler = class {
91798
91848
  { once: true }
91799
91849
  );
91800
91850
  }
91801
- applyPermissionMode(mode) {
91851
+ applyPermissionMode(mode, callbacks) {
91802
91852
  this.#deps.enqueueAsync(async () => {
91803
91853
  try {
91804
- await this.#deps.getSubprocess()?.setPermissionMode(mode);
91805
- if (mode === "bypass") {
91854
+ const subprocess = this.#deps.getSubprocess();
91855
+ await subprocess?.setPermissionMode(mode);
91856
+ if (mode === "bypass" && subprocess?.preservePendingPermissionRequestsInBypass !== true) {
91806
91857
  this.#approveAllPendingForBypass();
91807
91858
  }
91808
- this.#deps.getSendControlMessage()?.({
91809
- type: "task_settings_changed",
91810
- taskId: this.#deps.taskId,
91811
- settings: { permissionMode: mode }
91812
- });
91859
+ if (callbacks?.suppressBroadcast !== true) {
91860
+ this.#deps.getSendControlMessage()?.({
91861
+ type: "task_settings_changed",
91862
+ taskId: this.#deps.taskId,
91863
+ settings: { permissionMode: mode }
91864
+ });
91865
+ }
91866
+ callbacks?.onApplied?.();
91813
91867
  } catch (err3) {
91814
91868
  this.#deps.log({
91815
91869
  event: "apply_permission_mode_error",
91816
91870
  taskId: this.#deps.taskId,
91817
91871
  error: err3 instanceof Error ? err3.message : String(err3)
91818
91872
  });
91873
+ callbacks?.onRejected?.(err3);
91819
91874
  }
91820
91875
  });
91821
91876
  }
@@ -92204,8 +92259,8 @@ var ThreadControlController = class {
92204
92259
  get sendControlMessage() {
92205
92260
  return this.#sendControlMessage;
92206
92261
  }
92207
- applyPermissionMode(mode) {
92208
- this.#permissionHandler.applyPermissionMode(mode);
92262
+ applyPermissionMode(mode, callbacks) {
92263
+ this.#permissionHandler.applyPermissionMode(mode, callbacks);
92209
92264
  }
92210
92265
  get pendingPermissionCount() {
92211
92266
  return this.#permissionHandler.pendingCount;
@@ -92565,6 +92620,12 @@ var ThreadEventProcessor = class {
92565
92620
  async #handle(event) {
92566
92621
  const manager = this.#deps.getManager();
92567
92622
  switch (event.type) {
92623
+ case "permission_mode_applied":
92624
+ this.#deps.config.onPermissionModeApplied?.(event.mode);
92625
+ break;
92626
+ case "permission_mode_rejected":
92627
+ this.#deps.config.onPermissionModeRejected?.(event.mode);
92628
+ break;
92568
92629
  case "init_received":
92569
92630
  await this.#handleInitReceived(event);
92570
92631
  break;
@@ -92749,12 +92810,14 @@ var ThreadEventProcessor = class {
92749
92810
 
92750
92811
  // src/services/conversation/thread-settings-flush.ts
92751
92812
  var PermissionModeFlushError = class extends Error {
92813
+ mode;
92752
92814
  constructor(mode, cause) {
92753
92815
  super(`Could not apply permission mode ${mode}`, { cause });
92754
92816
  this.name = "PermissionModeFlushError";
92817
+ this.mode = mode;
92755
92818
  }
92756
92819
  };
92757
- async function reconcilePermissionMode(subprocess, settings, applied, onError) {
92820
+ async function reconcilePermissionMode(subprocess, settings, applied, onError, onApplied) {
92758
92821
  const mode = settings.permissionMode;
92759
92822
  if (mode === void 0) return;
92760
92823
  if (mode === applied.permissionMode && subprocess.reconcilePermissionModeBeforePrompt !== true) {
@@ -92763,12 +92826,13 @@ async function reconcilePermissionMode(subprocess, settings, applied, onError) {
92763
92826
  try {
92764
92827
  await subprocess.setPermissionMode(mode);
92765
92828
  applied.permissionMode = mode;
92829
+ onApplied?.(mode);
92766
92830
  } catch (error) {
92767
92831
  onError("permission_mode", error);
92768
92832
  throw new PermissionModeFlushError(mode, error);
92769
92833
  }
92770
92834
  }
92771
- async function flushSubprocessSettings(subprocess, settings, applied, onError) {
92835
+ async function flushSubprocessSettings(subprocess, settings, applied, onError, onPermissionModeApplied) {
92772
92836
  const attempt = async (kind, operation) => {
92773
92837
  try {
92774
92838
  await operation();
@@ -92776,7 +92840,7 @@ async function flushSubprocessSettings(subprocess, settings, applied, onError) {
92776
92840
  onError(kind, error);
92777
92841
  }
92778
92842
  };
92779
- await reconcilePermissionMode(subprocess, settings, applied, onError);
92843
+ await reconcilePermissionMode(subprocess, settings, applied, onError, onPermissionModeApplied);
92780
92844
  const selection = {
92781
92845
  model: settings.model,
92782
92846
  reasoningEffort: settings.reasoningEffort,
@@ -92831,9 +92895,15 @@ var ThreadSettingsController = class {
92831
92895
  merge(settings) {
92832
92896
  this.#latest = { ...this.#latest, ...settings };
92833
92897
  }
92834
- applyPermissionMode(mode) {
92898
+ applyPermissionMode(mode, callbacks) {
92835
92899
  this.#latest = { ...this.#latest, permissionMode: mode };
92836
- this.#deps.applyPermissionMode(mode);
92900
+ this.#deps.applyPermissionMode(mode, {
92901
+ onApplied: () => {
92902
+ callbacks?.onApplied?.();
92903
+ },
92904
+ onRejected: callbacks?.onRejected,
92905
+ suppressBroadcast: callbacks?.suppressBroadcast
92906
+ });
92837
92907
  }
92838
92908
  setModel(model) {
92839
92909
  this.#latest = { ...this.#latest, model };
@@ -92943,9 +93013,13 @@ var ThreadSettingsController = class {
92943
93013
  event: `thread_flush_${kind}_error`,
92944
93014
  threadId: this.#deps.config.threadId,
92945
93015
  error: error instanceof Error ? error.message : String(error)
92946
- })
93016
+ }),
93017
+ this.#deps.config.onPermissionModeApplied
92947
93018
  );
92948
93019
  }
93020
+ stagePermissionMode(mode) {
93021
+ this.#latest = { ...this.#latest, permissionMode: mode };
93022
+ }
92949
93023
  resetApplied() {
92950
93024
  this.#applied = {};
92951
93025
  }
@@ -92999,7 +93073,7 @@ var Thread = class {
92999
93073
  config,
93000
93074
  getSubprocess: () => this.#subprocess,
93001
93075
  getAgentSystem: () => this.#agentSystem,
93002
- applyPermissionMode: (mode) => this.#control.applyPermissionMode(mode),
93076
+ applyPermissionMode: (mode, callbacks2) => this.#control.applyPermissionMode(mode, callbacks2),
93003
93077
  respawnForProfileSwitch: () => {
93004
93078
  this.#subprocess?.close();
93005
93079
  this.resetSessionId();
@@ -93325,8 +93399,11 @@ var Thread = class {
93325
93399
  const heldCount = this.#rewindInput.cancelHumans();
93326
93400
  return this.#manager.cancelQueued() + heldCount;
93327
93401
  }
93328
- applyPermissionMode(mode) {
93329
- this.#settings.applyPermissionMode(mode);
93402
+ applyPermissionMode(mode, callbacks) {
93403
+ this.#settings.applyPermissionMode(mode, callbacks);
93404
+ }
93405
+ stagePermissionMode(mode) {
93406
+ this.#settings.stagePermissionMode(mode);
93330
93407
  }
93331
93408
  get pendingPermissionCount() {
93332
93409
  return this.#control.pendingPermissionCount;
@@ -93573,6 +93650,7 @@ var Thread = class {
93573
93650
  error: err3 instanceof Error ? err3.message : String(err3)
93574
93651
  });
93575
93652
  if (err3 instanceof PermissionModeFlushError) {
93653
+ this.#config.onPermissionModeRejected?.(err3.mode);
93576
93654
  this.#events.surfaceSdkError(`Message was not sent: ${err3.message}`);
93577
93655
  }
93578
93656
  }
@@ -93626,6 +93704,7 @@ var Thread = class {
93626
93704
  error: err3 instanceof Error ? err3.message : String(err3)
93627
93705
  });
93628
93706
  if (err3 instanceof PermissionModeFlushError) {
93707
+ this.#config.onPermissionModeRejected?.(err3.mode);
93629
93708
  this.#events.surfaceSdkError(`Message was not sent: ${err3.message}`);
93630
93709
  }
93631
93710
  }
@@ -98599,6 +98678,8 @@ var SideThreadRegistry = class {
98599
98678
  metricsCollector: this.#deps.metricsCollector,
98600
98679
  /** Cross-profile model switch detection — same wiring as the main thread. */
98601
98680
  resolveProfileForModel: (modelId) => profileRegistry.findByModelId(modelId)?.id ?? null,
98681
+ onPermissionModeApplied: this.#deps.onPermissionModeApplied,
98682
+ onPermissionModeRejected: this.#deps.onPermissionModeRejected,
98602
98683
  lookupToolDisplayName: this.#deps.lookupToolDisplayName
98603
98684
  },
98604
98685
  {
@@ -99609,6 +99690,8 @@ function isCompactionActivityEvent(event) {
99609
99690
  case "compaction_failed":
99610
99691
  case "compaction_started":
99611
99692
  case "init_received":
99693
+ case "permission_mode_applied":
99694
+ case "permission_mode_rejected":
99612
99695
  case "rate_limit":
99613
99696
  case "rate_limit_error":
99614
99697
  case "sdk_error":
@@ -99790,6 +99873,8 @@ function handleMainThreadEvent(ctx, event) {
99790
99873
  break;
99791
99874
  case "stream_activity":
99792
99875
  case "available_commands_updated":
99876
+ case "permission_mode_applied":
99877
+ case "permission_mode_rejected":
99793
99878
  break;
99794
99879
  case "tool_execution_started":
99795
99880
  case "tool_execution_settled":
@@ -104076,6 +104161,7 @@ var Task = class {
104076
104161
  #originalCwd;
104077
104162
  #cwdEscapeWarningActive = false;
104078
104163
  #latestSettings = {};
104164
+ #confirmedPermissionMode = "default";
104079
104165
  #pendingMessageMeta = [];
104080
104166
  #pendingWorktreeToolUseIds = /* @__PURE__ */ new Set();
104081
104167
  #lastAgentSeqNo = null;
@@ -104672,6 +104758,8 @@ var Task = class {
104672
104758
  * subprocess. Returns null for unknown models.
104673
104759
  */
104674
104760
  resolveProfileForModel: (modelId) => profileRegistry.findByModelId(modelId)?.id ?? null,
104761
+ onPermissionModeApplied: (mode) => this.#handlePermissionModeApplied(mode),
104762
+ onPermissionModeRejected: (mode) => this.#handlePermissionModeRejected(mode),
104675
104763
  /*
104676
104764
  * Thread applies `?? null` internally (thread.ts), so pass through raw
104677
104765
  * to avoid an extra `??` in this already-at-budget constructor.
@@ -104718,6 +104806,8 @@ var Task = class {
104718
104806
  getThreadMeta: (threadId) => this.#shiftThreadMeta(threadId),
104719
104807
  prepareMessages: (threadId, channelId, messages) => this.#prepareSideMessages(threadId, channelId, messages),
104720
104808
  onThreadTurnComplete: (threadId) => this.notifyThreadTurnComplete(threadId),
104809
+ onPermissionModeApplied: (mode) => this.#handlePermissionModeApplied(mode),
104810
+ onPermissionModeRejected: (mode) => this.#handlePermissionModeRejected(mode),
104721
104811
  lookupToolDisplayName: deps.lookupToolDisplayName
104722
104812
  });
104723
104813
  this.#commentNotificationWatcher = this.#buildCommentNotificationWatcher();
@@ -104917,8 +105007,12 @@ var Task = class {
104917
105007
  * compaction occurred on those.
104918
105008
  */
104919
105009
  applyCrossRuntimeSwitch(newRuntimeId, pendingHandoff, mode) {
105010
+ const previousRuntimeId = this.#runtimeId;
104920
105011
  this.#deps.onCrossRuntimeSwitch?.(this.#deps.taskId, this.#runtimeId, newRuntimeId);
104921
105012
  applyCrossRuntimeSwitch(this.#crossRuntimeCtx(), newRuntimeId, pendingHandoff, mode);
105013
+ if (previousRuntimeId !== this.#runtimeId) {
105014
+ this.#confirmedPermissionMode = this.#runtimeId === "devin" ? "default" : this.#latestSettings.permissionMode ?? "default";
105015
+ }
104922
105016
  }
104923
105017
  /**
104924
105018
  * The wire `handoff_status.mode` the daemon ran for the most recent
@@ -105233,8 +105327,75 @@ var Task = class {
105233
105327
  currentReasoningEffort: this.#latestSettings.reasoningEffort,
105234
105328
  log: this.#deps.log
105235
105329
  });
105330
+ const primary = this.#getPrimary();
105331
+ if (normalized.permissionMode !== void 0 && this.#runtimeId === "devin" && primary.currentSubprocess?.permissionModeRequiresNativeAck === true) {
105332
+ const mode = normalized.permissionMode;
105333
+ const immediate = { ...normalized };
105334
+ delete immediate.permissionMode;
105335
+ if (Object.values(immediate).some((value) => value !== void 0)) {
105336
+ this.#latestSettings = { ...this.#latestSettings, ...immediate };
105337
+ primary.applySettings(immediate);
105338
+ this.#propagateToSideThreads(
105339
+ (thread) => thread.applySettings(immediate),
105340
+ "side_thread_apply_settings_failed"
105341
+ );
105342
+ this.#deps.onSettingsApplied(immediate);
105343
+ }
105344
+ primary.applyPermissionMode(mode, {
105345
+ suppressBroadcast: true,
105346
+ onApplied: () => {
105347
+ this.#confirmedPermissionMode = mode;
105348
+ this.#latestSettings = { ...this.#latestSettings, permissionMode: mode };
105349
+ this.#propagateToSideThreads(
105350
+ (thread) => thread.applyPermissionMode(mode, { suppressBroadcast: true }),
105351
+ "side_thread_apply_permission_mode_failed"
105352
+ );
105353
+ this.#deps.onSettingsApplied({ permissionMode: mode });
105354
+ },
105355
+ onRejected: () => {
105356
+ const rollback = this.#confirmedPermissionMode === mode ? "default" : this.#confirmedPermissionMode;
105357
+ this.#confirmedPermissionMode = rollback;
105358
+ this.#latestSettings = {
105359
+ ...this.#latestSettings,
105360
+ permissionMode: rollback
105361
+ };
105362
+ primary.stagePermissionMode(rollback);
105363
+ this.#propagateToSideThreads(
105364
+ (thread) => thread.stagePermissionMode(rollback),
105365
+ "side_thread_stage_permission_mode_rollback_failed"
105366
+ );
105367
+ this.#deps.onSettingsApplied({
105368
+ permissionMode: rollback
105369
+ });
105370
+ }
105371
+ });
105372
+ return;
105373
+ }
105374
+ if (normalized.permissionMode !== void 0 && this.#runtimeId === "devin") {
105375
+ const mode = normalized.permissionMode;
105376
+ const immediate = { ...normalized };
105377
+ delete immediate.permissionMode;
105378
+ this.#latestSettings = { ...this.#latestSettings, ...immediate, permissionMode: mode };
105379
+ primary.stagePermissionMode(mode);
105380
+ this.#propagateToSideThreads(
105381
+ (thread) => thread.stagePermissionMode(mode),
105382
+ "side_thread_stage_permission_mode_failed"
105383
+ );
105384
+ if (Object.values(immediate).some((value) => value !== void 0)) {
105385
+ primary.applySettings(immediate);
105386
+ this.#propagateToSideThreads(
105387
+ (thread) => thread.applySettings(immediate),
105388
+ "side_thread_apply_settings_failed"
105389
+ );
105390
+ this.#deps.onSettingsApplied(immediate);
105391
+ }
105392
+ return;
105393
+ }
105236
105394
  this.#latestSettings = { ...this.#latestSettings, ...normalized };
105237
- this.#getPrimary().applySettings(normalized);
105395
+ if (normalized.permissionMode !== void 0 && this.#runtimeId !== "devin") {
105396
+ this.#confirmedPermissionMode = normalized.permissionMode;
105397
+ }
105398
+ primary.applySettings(normalized);
105238
105399
  this.#propagateToSideThreads(
105239
105400
  (t) => t.applySettings(normalized),
105240
105401
  "side_thread_apply_settings_failed"
@@ -105244,6 +105405,10 @@ var Task = class {
105244
105405
  }
105245
105406
  this.#deps.onSettingsApplied(normalized);
105246
105407
  }
105408
+ /** A persisted Devin mode was acknowledged in the prior native session. */
105409
+ seedConfirmedPermissionMode(mode) {
105410
+ this.#confirmedPermissionMode = mode;
105411
+ }
105247
105412
  /**
105248
105413
  * Stage settings for the next spawn without live-applying them to the current
105249
105414
  * subprocess. Used by cross-runtime model switches: the destination model must
@@ -105333,6 +105498,24 @@ var Task = class {
105333
105498
  }
105334
105499
  }
105335
105500
  }
105501
+ #handlePermissionModeApplied(mode) {
105502
+ if (this.#confirmedPermissionMode === mode) return;
105503
+ this.#confirmedPermissionMode = mode;
105504
+ this.#latestSettings = { ...this.#latestSettings, permissionMode: mode };
105505
+ this.#deps.onSettingsApplied({ permissionMode: mode });
105506
+ }
105507
+ #handlePermissionModeRejected(mode) {
105508
+ if (this.#latestSettings.permissionMode !== mode) return;
105509
+ const rollback = this.#confirmedPermissionMode === mode ? "default" : this.#confirmedPermissionMode;
105510
+ this.#confirmedPermissionMode = rollback;
105511
+ this.#latestSettings = { ...this.#latestSettings, permissionMode: rollback };
105512
+ this.#getPrimary().stagePermissionMode(rollback);
105513
+ this.#propagateToSideThreads(
105514
+ (thread) => thread.stagePermissionMode(rollback),
105515
+ "side_thread_stage_permission_mode_rollback_failed"
105516
+ );
105517
+ this.#deps.onSettingsApplied({ permissionMode: rollback });
105518
+ }
105336
105519
  async setMcpServers(servers) {
105337
105520
  const mainResult = await this.#getPrimary().setMcpServers(servers);
105338
105521
  for (const thread of this.#sideThreads.liveThreads()) {
@@ -115607,8 +115790,11 @@ async function runServeBody(options, captureRefs) {
115607
115790
  });
115608
115791
  }
115609
115792
  })();
115793
+ let fullDetectionDevinAuthBaseline = daemon.capabilities?.devinAuth;
115610
115794
  const applyFreshCapabilities = async (fresh) => {
115611
- await applyFullSnapshot(fresh);
115795
+ await applyFullSnapshot(fresh, {
115796
+ devinAuthBaseline: fullDetectionDevinAuthBaseline
115797
+ });
115612
115798
  if (disposed) return;
115613
115799
  await retryProxyAndCoordinatorWiring();
115614
115800
  await broadcastProfileSnapshot();
@@ -115616,6 +115802,7 @@ async function runServeBody(options, captureRefs) {
115616
115802
  const capabilityRefresh = createCapabilityRefreshCoordinator({
115617
115803
  isDisposed: () => disposed,
115618
115804
  detectFull: async () => {
115805
+ fullDetectionDevinAuthBaseline = daemon.capabilities?.devinAuth;
115619
115806
  const userSettings = await daemon.userSettingsStore.getSettings();
115620
115807
  return withLabelAsync("awaitCapabilityWorker", async () => {
115621
115808
  await new Promise((r) => {
@@ -116045,4 +116232,4 @@ export {
116045
116232
  decideWorkspaceScope,
116046
116233
  serve
116047
116234
  };
116048
- //# sourceMappingURL=serve-VBMV5LEN.js.map
116235
+ //# sourceMappingURL=serve-RFNU5ZMH.js.map