@sema-agent/client-core 0.110.0 → 0.112.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 (243) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/README.md +59 -18
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +25 -4
  6. package/dist/adapt/toolCards.d.ts +3 -1
  7. package/dist/adapt/toolCards.js +4 -2
  8. package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
  9. package/dist/adapter/activeRunSelfHeal.js +171 -3
  10. package/dist/adapter/downstream/eventToSdkMessage.js +10 -10
  11. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
  12. package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
  13. package/dist/adapter/runStream.d.ts +5 -0
  14. package/dist/adapter/runStream.js +15 -3
  15. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  16. package/dist/attachmentRoute.d.ts +20 -0
  17. package/dist/attachmentRoute.js +39 -0
  18. package/dist/attachmentsWireCaps.js +1 -1
  19. package/dist/builtinOutputStyles.d.ts +12 -0
  20. package/dist/builtinOutputStyles.js +147 -0
  21. package/dist/checkpointRowVerdict.d.ts +3 -0
  22. package/dist/checkpointRowVerdict.js +14 -1
  23. package/dist/compactionStatus.d.ts +9 -0
  24. package/dist/compactionStatus.js +29 -5
  25. package/dist/consoleCaPin.d.ts +136 -0
  26. package/dist/consoleCaPin.js +827 -0
  27. package/dist/controlRouter.js +2 -1
  28. package/dist/coreValuePorts.d.ts +39 -1
  29. package/dist/coreValuePorts.js +10 -0
  30. package/dist/decideReceipt.d.ts +2 -1
  31. package/dist/deviceRebind.js +3 -3
  32. package/dist/displayUntrusted.d.ts +1 -0
  33. package/dist/displayUntrusted.js +6 -0
  34. package/dist/effectiveFacts.d.ts +9 -0
  35. package/dist/effectiveFacts.js +33 -0
  36. package/dist/engineAddress.d.ts +1 -0
  37. package/dist/engineAddress.js +4 -1
  38. package/dist/engineCredentialRelease.d.ts +54 -0
  39. package/dist/engineCredentialRelease.js +146 -0
  40. package/dist/engineErrorCodes.d.ts +1 -0
  41. package/dist/engineErrorCodes.js +1 -0
  42. package/dist/engineHealthProbeVerdict.d.ts +24 -0
  43. package/dist/engineHealthProbeVerdict.js +123 -0
  44. package/dist/engineNoticeCodes.js +5 -7
  45. package/dist/engineRepoint.d.ts +12 -5
  46. package/dist/engineRepoint.js +44 -19
  47. package/dist/engineRowRef.d.ts +3 -0
  48. package/dist/engineRowRef.js +11 -1
  49. package/dist/engineSwapCrossing.d.ts +57 -0
  50. package/dist/engineSwapCrossing.js +297 -0
  51. package/dist/engineUnavailable.d.ts +45 -0
  52. package/dist/engineUnavailable.js +496 -0
  53. package/dist/engineUrl.js +4 -2
  54. package/dist/eventLeg.js +3 -3
  55. package/dist/gateFailureCodes.js +2 -2
  56. package/dist/generated/sdkFactTables.d.ts +2 -1
  57. package/dist/generated/sdkFactTables.js +14 -0
  58. package/dist/generated/settingsSchemaFns.d.ts +1 -0
  59. package/dist/generated/settingsSchemaFns.js +3 -0
  60. package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
  61. package/dist/generated/settingsSchemaTables.d.ts +56 -0
  62. package/dist/generated/settingsSchemaTables.js +19 -0
  63. package/dist/generated/settingsSchemaTypes.d.ts +23 -13
  64. package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
  65. package/dist/generated/settingsSchemaUrlShape.js +17 -0
  66. package/dist/hitl/approvalDelivery.d.ts +1 -0
  67. package/dist/hitl/approvalDelivery.js +1 -1
  68. package/dist/hitl/approvalResolution.d.ts +1 -1
  69. package/dist/hitl/approvalResolution.js +2 -1
  70. package/dist/hitl/askGateWire.js +5 -6
  71. package/dist/hitl/decisionRefusal.d.ts +6 -0
  72. package/dist/hitl/decisionRefusal.js +19 -10
  73. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  74. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  75. package/dist/hitl/decisionSend.d.ts +7 -0
  76. package/dist/hitl/decisionSend.js +34 -12
  77. package/dist/hitl/editedApprovalAcceptance.js +1 -1
  78. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  79. package/dist/hitl/elicitationRefusal.js +23 -0
  80. package/dist/hitl/frameRouter.d.ts +2 -2
  81. package/dist/hitl/frameRouter.js +8 -15
  82. package/dist/hitl/gateIdentity.d.ts +5 -0
  83. package/dist/hitl/gateIdentity.js +23 -0
  84. package/dist/hitl/hitlBridge.d.ts +6 -0
  85. package/dist/hitl/hitlBridge.js +10 -4
  86. package/dist/hitl/hitlHostSurface.d.ts +2 -1
  87. package/dist/hitl/hitlHostSurface.js +7 -2
  88. package/dist/hitl/localAllowRedemption.d.ts +70 -0
  89. package/dist/hitl/localAllowRedemption.js +276 -0
  90. package/dist/hitl/parkOwnership.js +2 -1
  91. package/dist/hitl/parkResolver.js +14 -4
  92. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  93. package/dist/hitl/persistedRulesWire.js +70 -0
  94. package/dist/hitl/planReviewWire.d.ts +3 -0
  95. package/dist/hitl/planReviewWire.js +23 -6
  96. package/dist/hitl/policyDenyCause.d.ts +1 -1
  97. package/dist/hitl/policyDenyCause.js +1 -1
  98. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  99. package/dist/hitl/resumeRunningCard.js +1 -0
  100. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  101. package/dist/hitl/screenCardCarriers.js +41 -0
  102. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  103. package/dist/hitl/screenDecisionCards.js +363 -0
  104. package/dist/hitl/sessionPolicyWire.js +2 -2
  105. package/dist/hitl/standingRows.d.ts +6 -2
  106. package/dist/hitl/standingRows.js +27 -2
  107. package/dist/hitl/toolApprovalWire.d.ts +30 -8
  108. package/dist/hitl/toolApprovalWire.js +264 -121
  109. package/dist/hooksReaderGenerations.js +1 -1
  110. package/dist/hooksWireCaps.js +3 -3
  111. package/dist/hostSettingNoticeArm.d.ts +3 -0
  112. package/dist/hostSettingNoticeArm.js +12 -0
  113. package/dist/hostTurnSettings.d.ts +3 -0
  114. package/dist/hostTurnSettings.js +38 -5
  115. package/dist/humanTurnRead.js +11 -1
  116. package/dist/i18n/messages.en.d.ts +292 -0
  117. package/dist/i18n/messages.en.js +298 -7
  118. package/dist/i18n/messages.zh.js +316 -6
  119. package/dist/i18n/translate.d.ts +3 -0
  120. package/dist/i18n/translate.js +78 -10
  121. package/dist/i18n/zh.d.ts +1 -0
  122. package/dist/i18n/zh.js +1 -0
  123. package/dist/index.d.ts +71 -20
  124. package/dist/index.js +44 -13
  125. package/dist/interactiveHalt.js +2 -1
  126. package/dist/interactiveInterrupt.js +20 -2
  127. package/dist/liveModelCatalog.d.ts +4 -0
  128. package/dist/liveModelCatalog.js +12 -3
  129. package/dist/localConfig/documents.d.ts +61 -0
  130. package/dist/localConfig/documents.js +51 -0
  131. package/dist/localConfig/kernel.d.ts +149 -0
  132. package/dist/localConfig/kernel.js +597 -0
  133. package/dist/localConfig/mcp/edit.d.ts +161 -0
  134. package/dist/localConfig/mcp/edit.js +846 -0
  135. package/dist/localConfig/mcp/servers.d.ts +165 -0
  136. package/dist/localConfig/mcp/servers.js +776 -0
  137. package/dist/localConfig/mcp/views.d.ts +170 -0
  138. package/dist/localConfig/mcp/views.js +429 -0
  139. package/dist/localConfig/modelPool.d.ts +262 -0
  140. package/dist/localConfig/modelPool.js +1489 -0
  141. package/dist/localConfig/modelPoolWords.d.ts +13 -0
  142. package/dist/localConfig/modelPoolWords.js +20 -0
  143. package/dist/localConfig/receiptShape.d.ts +7 -0
  144. package/dist/localConfig/receiptShape.js +135 -0
  145. package/dist/localConfig/settingsWrite.d.ts +127 -0
  146. package/dist/localConfig/settingsWrite.js +237 -0
  147. package/dist/localConfig/skills.d.ts +124 -0
  148. package/dist/localConfig/skills.js +397 -0
  149. package/dist/localConfig/skillsWords.d.ts +10 -0
  150. package/dist/localConfig/skillsWords.js +5 -0
  151. package/dist/localEngineSeatReading.d.ts +2 -2
  152. package/dist/localEngineSeatReading.js +5 -3
  153. package/dist/localEngineSpawn.d.ts +0 -7
  154. package/dist/localEngineSpawn.js +0 -92
  155. package/dist/localEngineStep.d.ts +2 -2
  156. package/dist/localEngineStep.js +2 -0
  157. package/dist/mcpProbeWire.js +2 -1
  158. package/dist/mcpReconnect.js +3 -3
  159. package/dist/mcpWireCaps.js +1 -1
  160. package/dist/memoryVerbsWire.js +3 -2
  161. package/dist/model/engineModelWords.d.ts +3 -0
  162. package/dist/model/engineModelWords.js +13 -0
  163. package/dist/model/onboardJudgement.d.ts +14 -4
  164. package/dist/model/onboardJudgement.js +91 -139
  165. package/dist/model/subagentModelWord.d.ts +21 -2
  166. package/dist/model/subagentModelWord.js +100 -29
  167. package/dist/modelCapabilityProbe.js +3 -40
  168. package/dist/modelConfigGapNotice.d.ts +20 -0
  169. package/dist/modelConfigGapNotice.js +102 -0
  170. package/dist/modsKernelIdentity.js +2 -47
  171. package/dist/notifications.d.ts +19 -0
  172. package/dist/notifications.js +114 -12
  173. package/dist/ownKey.d.ts +7 -0
  174. package/dist/ownKey.js +71 -8
  175. package/dist/pendingUserRow.d.ts +4 -0
  176. package/dist/pendingUserRow.js +16 -0
  177. package/dist/permissionsDeclaration.d.ts +3 -1
  178. package/dist/permissionsDeclaration.js +33 -9
  179. package/dist/planReviewCardFacts.d.ts +3 -0
  180. package/dist/planReviewCardFacts.js +52 -2
  181. package/dist/pluginHooksWire.d.ts +9 -1
  182. package/dist/pluginHooksWire.js +40 -6
  183. package/dist/printToolResultFrame.js +4 -2
  184. package/dist/promptBlockedCopy.js +12 -7
  185. package/dist/promptSuggestionBatch.d.ts +7 -0
  186. package/dist/promptSuggestionBatch.js +9 -0
  187. package/dist/promptSuggestionsTail.d.ts +39 -0
  188. package/dist/promptSuggestionsTail.js +116 -0
  189. package/dist/request/taskRequest.js +10 -6
  190. package/dist/requestCoordinate.d.ts +1 -0
  191. package/dist/requestCoordinate.js +13 -0
  192. package/dist/resumeRefusalCopy.d.ts +3 -1
  193. package/dist/resumeRefusalCopy.js +12 -4
  194. package/dist/rewindArchiveReading.js +4 -4
  195. package/dist/runCancelContext.d.ts +2 -1
  196. package/dist/runCancelContext.js +1 -0
  197. package/dist/runLedgerSnapshot.js +3 -3
  198. package/dist/runPermissionModeSwitch.js +3 -4
  199. package/dist/scenarioRefusal.js +11 -13
  200. package/dist/scenarioSkills.js +11 -6
  201. package/dist/seam.d.ts +16 -2
  202. package/dist/seam.js +10 -0
  203. package/dist/seatContract.d.ts +259 -9
  204. package/dist/seatContract.js +444 -15
  205. package/dist/sessionCostReading.d.ts +70 -0
  206. package/dist/sessionCostReading.js +150 -0
  207. package/dist/sessionHooksRoster.d.ts +4 -0
  208. package/dist/sessionHooksRoster.js +158 -0
  209. package/dist/sessionIdSegment.d.ts +1 -0
  210. package/dist/sessionIdSegment.js +4 -1
  211. package/dist/sessionMemoryErase.js +2 -1
  212. package/dist/sessionMemoryStatus.js +2 -2
  213. package/dist/sessionReadRedaction.d.ts +7 -0
  214. package/dist/sessionReadRedaction.js +26 -0
  215. package/dist/sessionSyncFileHistory.js +8 -9
  216. package/dist/settingsHooksSalvage.js +2 -1
  217. package/dist/sha256Bytes.d.ts +1 -0
  218. package/dist/sha256Bytes.js +46 -0
  219. package/dist/skillOverrideChain.d.ts +83 -0
  220. package/dist/skillOverrideChain.js +378 -0
  221. package/dist/skillsWireCaps.d.ts +3 -0
  222. package/dist/skillsWireCaps.js +16 -1
  223. package/dist/sseIdleTriage.js +2 -1
  224. package/dist/storeDurabilityWord.d.ts +15 -0
  225. package/dist/storeDurabilityWord.js +18 -0
  226. package/dist/storePostureCapability.d.ts +7 -3
  227. package/dist/storePostureCapability.js +69 -39
  228. package/dist/subagent/engineSubagentResume.js +2 -2
  229. package/dist/subagent/engineSubagentTail.js +2 -1
  230. package/dist/subagentContentStore.d.ts +11 -0
  231. package/dist/subagentContentStore.js +28 -2
  232. package/dist/toolResult.d.ts +1 -0
  233. package/dist/toolResult.js +2 -0
  234. package/dist/turnFailurePresentation.js +4 -10
  235. package/dist/userAttachmentEnvelope.d.ts +2 -1
  236. package/dist/userAttachmentEnvelope.js +10 -0
  237. package/dist/wireErrorTriage.js +2 -1
  238. package/dist/wireFailureShape.d.ts +4 -0
  239. package/dist/wireFailureShape.js +141 -19
  240. package/dist/wireRefusalCopy.d.ts +6 -5
  241. package/dist/wireRefusalCopy.js +49 -92
  242. package/docs/INTEGRATION-CLIENTS.md +2718 -17
  243. package/package.json +7 -3
@@ -0,0 +1,846 @@
1
+ import { applyLocalConfigIntent, previewLocalConfigIntent, localConfigAccess, } from '../kernel.js';
2
+ import { mcpServerNameVerdict } from '../../mcpWireCaps.js';
3
+ import { readHostSettingsOnce } from '../../hostSettingsSource.js';
4
+ import { settingsWriteStep } from '../settingsWrite.js';
5
+ import { settingsCredentialKeyPaths } from '../../settingsCredentialKeyPaths.js';
6
+ import { MCP_SERVER_SCOPES, USER_SCOPE_PATH, PROJECT_FILE_PATH, localScopePath, judgeTable, ownData, stringListOf, subTableOf, documentOf, fileReadingOf, nameListOf, } from './servers.js';
7
+ export const MCP_SERVER_EDIT_OPS = Object.freeze(['add', 'update', 'remove', 'setEnabled', 'setProjectApproval', 'importFromSibling']);
8
+ const REFUSAL_REASON = Object.freeze({
9
+ edit_malformed: 'invalid_request',
10
+ name_unusable: 'name_unusable',
11
+ name_reserved: 'name_reserved',
12
+ mcp_entry_invalid: 'mcp_entry_invalid',
13
+ name_taken: 'already_exists',
14
+ not_found: 'not_found',
15
+ ambiguous_scope: 'ambiguous_scope',
16
+ scope_not_offered: 'scope_unwritable',
17
+ scope_unreadable: 'scope_unreadable',
18
+ policy_locked: 'policy_denied',
19
+ base_conflict: 'base_conflict',
20
+ credentials_need_reentry: 'credentials_need_reentry',
21
+ untrusted_folder: 'untrusted_folder',
22
+ });
23
+ export const refusalReasonOf = (w) => REFUSAL_REASON[w];
24
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
25
+ const own = (v, k) => {
26
+ const r = ownData(v, k);
27
+ return r.ok ? r.value : undefined;
28
+ };
29
+ const def = (o, k, v) => {
30
+ Object.defineProperty(o, k, { value: v, writable: true, enumerable: true, configurable: true });
31
+ };
32
+ function copyLayer(v) {
33
+ const out = {};
34
+ if (!isRecord(v))
35
+ return out;
36
+ for (const k of Object.keys(v)) {
37
+ const d = Object.getOwnPropertyDescriptor(v, k);
38
+ if (d !== undefined)
39
+ Object.defineProperty(out, k, { ...d, enumerable: true, configurable: true });
40
+ }
41
+ return out;
42
+ }
43
+ export function withPath(doc, path, value) {
44
+ const root = copyLayer(doc);
45
+ let box = root;
46
+ for (let i = 0; i < path.length - 1; i++) {
47
+ const seg = path[i];
48
+ const r = ownData(box, seg);
49
+ if (!r.ok)
50
+ return undefined;
51
+ if (r.value !== undefined && !isRecord(r.value))
52
+ return undefined;
53
+ const next = copyLayer(r.value);
54
+ def(box, seg, next);
55
+ box = next;
56
+ }
57
+ const last = path[path.length - 1];
58
+ if (value === undefined)
59
+ delete box[last];
60
+ else
61
+ def(box, last, value);
62
+ return root;
63
+ }
64
+ function tableWith(table, name, value) {
65
+ const out = {};
66
+ let placed = false;
67
+ if (table !== undefined) {
68
+ for (const k of Object.keys(table)) {
69
+ if (k === name) {
70
+ placed = true;
71
+ if (value !== undefined)
72
+ def(out, k, value);
73
+ continue;
74
+ }
75
+ const d = Object.getOwnPropertyDescriptor(table, k);
76
+ if (d !== undefined)
77
+ Object.defineProperty(out, k, { ...d, enumerable: true, configurable: true });
78
+ }
79
+ }
80
+ if (!placed && value !== undefined)
81
+ def(out, name, value);
82
+ return out;
83
+ }
84
+ const docOf = (fresh, d) => documentOf(fresh[d]);
85
+ function placeOf(scope, projectKey) {
86
+ if (scope === 'user')
87
+ return { doc: 'globalConfig', path: USER_SCOPE_PATH };
88
+ if (scope === 'project')
89
+ return { doc: 'projectMcp', path: PROJECT_FILE_PATH };
90
+ return projectKey === undefined ? undefined : { doc: 'globalConfig', path: localScopePath(projectKey) };
91
+ }
92
+ export const mcpRowBaseOf = (revision) => (revision === null ? null : `rev:${revision}`);
93
+ const ADD_KEYS = Object.freeze(['op', 'name', 'entry', 'scope', 'onNameTaken']);
94
+ const UPDATE_KEYS = Object.freeze(['op', 'name', 'scope', 'base', 'set', 'unset', 'env', 'headers', 'keep']);
95
+ const REMOVE_KEYS = Object.freeze(['op', 'name', 'scope']);
96
+ const ENABLE_KEYS = Object.freeze(['op', 'name', 'enabled']);
97
+ const APPROVAL_KEYS = Object.freeze(['op', 'name', 'approval']);
98
+ const IMPORT_KEYS = Object.freeze(['op', 'entries', 'scope']);
99
+ const isScope = (v) => MCP_SERVER_SCOPES.some((s) => s === v);
100
+ function plainData(v, depth = 0) {
101
+ if (depth > 16)
102
+ return false;
103
+ if (v === null || typeof v === 'string' || typeof v === 'boolean' || (typeof v === 'number' && Number.isFinite(v)))
104
+ return true;
105
+ if (typeof v !== 'object')
106
+ return false;
107
+ try {
108
+ if (!Array.isArray(v)) {
109
+ const p = Object.getPrototypeOf(v);
110
+ if (p !== null && Object.getPrototypeOf(p) !== null)
111
+ return false;
112
+ }
113
+ for (const k of Reflect.ownKeys(v)) {
114
+ if (typeof k !== 'string')
115
+ return false;
116
+ const d = Object.getOwnPropertyDescriptor(v, k);
117
+ if (d === undefined)
118
+ continue;
119
+ if (Array.isArray(v) && k === 'length')
120
+ continue;
121
+ if (!Object.hasOwn(d, 'value') || d.enumerable !== true || !plainData(d.value, depth + 1))
122
+ return false;
123
+ }
124
+ return true;
125
+ }
126
+ catch {
127
+ return false;
128
+ }
129
+ }
130
+ function secretMapOk(v) {
131
+ if (v === undefined)
132
+ return true;
133
+ if (!isRecord(v) || !plainData(v) || Object.keys(v).some((k) => k !== 'set' && k !== 'delete'))
134
+ return false;
135
+ const set = own(v, 'set');
136
+ if (set !== undefined && (!isRecord(set) || Object.keys(set).some((k) => typeof own(set, k) !== 'string')))
137
+ return false;
138
+ const del = own(v, 'delete');
139
+ return del === undefined || stringListOf(del) !== undefined;
140
+ }
141
+ export function shapeMcpServerEdit(v) {
142
+ try {
143
+ if (!isRecord(v) || !plainData(v))
144
+ return { ok: false, path: null };
145
+ const op = own(v, 'op');
146
+ const keysOk = (allowed) => Object.keys(v).find((k) => !allowed.includes(k));
147
+ const name = own(v, 'name');
148
+ const nameOk = typeof name === 'string';
149
+ switch (op) {
150
+ case 'add': {
151
+ const extra = keysOk(ADD_KEYS);
152
+ if (extra !== undefined)
153
+ return { ok: false, path: extra };
154
+ if (!nameOk)
155
+ return { ok: false, path: 'name' };
156
+ if (!isRecord(own(v, 'entry')))
157
+ return { ok: false, path: 'entry' };
158
+ if (!isScope(own(v, 'scope')))
159
+ return { ok: false, path: 'scope' };
160
+ const t = own(v, 'onNameTaken');
161
+ if (t !== undefined && t !== 'refuse' && t !== 'replace' && t !== 'rename')
162
+ return { ok: false, path: 'onNameTaken' };
163
+ return { ok: true, edit: v };
164
+ }
165
+ case 'update': {
166
+ const extra = keysOk(UPDATE_KEYS);
167
+ if (extra !== undefined)
168
+ return { ok: false, path: extra };
169
+ if (!nameOk)
170
+ return { ok: false, path: 'name' };
171
+ if (!isScope(own(v, 'scope')))
172
+ return { ok: false, path: 'scope' };
173
+ if (typeof own(v, 'base') !== 'string' || own(v, 'base') === '')
174
+ return { ok: false, path: 'base' };
175
+ const set = own(v, 'set');
176
+ if (set !== undefined && (!isRecord(set) || Object.keys(set).some((k) => k === 'env' || k === 'headers')))
177
+ return { ok: false, path: 'set' };
178
+ const unset = own(v, 'unset');
179
+ const unsetList = unset === undefined ? [] : stringListOf(unset);
180
+ if (unsetList === undefined)
181
+ return { ok: false, path: 'unset' };
182
+ if (!secretMapOk(own(v, 'env')))
183
+ return { ok: false, path: 'env' };
184
+ if (!secretMapOk(own(v, 'headers')))
185
+ return { ok: false, path: 'headers' };
186
+ if (unsetList.some((k) => (isRecord(set) && Object.hasOwn(set, k)) || ((k === 'env' || k === 'headers') && own(v, k) !== undefined)))
187
+ return { ok: false, path: 'unset' };
188
+ const keep = own(v, 'keep');
189
+ if (keep !== undefined && (!isRecord(keep) || typeof own(keep, 'base') !== 'string' || stringListOf(own(keep, 'members')) === undefined || Object.keys(keep).some((k) => k !== 'base' && k !== 'members')))
190
+ return { ok: false, path: 'keep' };
191
+ return { ok: true, edit: v };
192
+ }
193
+ case 'remove': {
194
+ const extra = keysOk(REMOVE_KEYS);
195
+ if (extra !== undefined)
196
+ return { ok: false, path: extra };
197
+ if (!nameOk)
198
+ return { ok: false, path: 'name' };
199
+ const scope = own(v, 'scope');
200
+ if (scope !== undefined && !isScope(scope))
201
+ return { ok: false, path: 'scope' };
202
+ return { ok: true, edit: v };
203
+ }
204
+ case 'setEnabled': {
205
+ const extra = keysOk(ENABLE_KEYS);
206
+ if (extra !== undefined)
207
+ return { ok: false, path: extra };
208
+ if (!nameOk)
209
+ return { ok: false, path: 'name' };
210
+ if (typeof own(v, 'enabled') !== 'boolean')
211
+ return { ok: false, path: 'enabled' };
212
+ return { ok: true, edit: v };
213
+ }
214
+ case 'setProjectApproval': {
215
+ const extra = keysOk(APPROVAL_KEYS);
216
+ if (extra !== undefined)
217
+ return { ok: false, path: extra };
218
+ if (!nameOk)
219
+ return { ok: false, path: 'name' };
220
+ const a = own(v, 'approval');
221
+ if (a !== 'approve' && a !== 'reject' && a !== 'clear')
222
+ return { ok: false, path: 'approval' };
223
+ return { ok: true, edit: v };
224
+ }
225
+ case 'importFromSibling': {
226
+ const extra = keysOk(IMPORT_KEYS);
227
+ if (extra !== undefined)
228
+ return { ok: false, path: extra };
229
+ const entries = own(v, 'entries');
230
+ if (!isRecord(entries) || Object.keys(entries).length === 0 || Object.keys(entries).length > 256)
231
+ return { ok: false, path: 'entries' };
232
+ if (!isScope(own(v, 'scope')))
233
+ return { ok: false, path: 'scope' };
234
+ return { ok: true, edit: v };
235
+ }
236
+ default:
237
+ return { ok: false, path: 'op' };
238
+ }
239
+ }
240
+ catch {
241
+ return { ok: false, path: null };
242
+ }
243
+ }
244
+ const refusedOutcome = (detail, notices = [], plan) => Object.freeze({
245
+ receipt: Object.freeze({ kind: 'refused', reason: refusalReasonOf(detail.refusal) }),
246
+ refusal: Object.freeze(detail),
247
+ ...(plan === undefined ? {} : { plan }),
248
+ notices: Object.freeze([...notices]),
249
+ });
250
+ function nameRefusal(name, siblings, reserved) {
251
+ if (reserved.has(name))
252
+ return { refusal: 'name_reserved', by: 'host' };
253
+ const v = mcpServerNameVerdict(name, siblings);
254
+ if (v.usable)
255
+ return undefined;
256
+ return v.reason === 'name_reserved' ? { refusal: 'name_reserved', by: 'package' } : { refusal: 'name_unusable', nameReason: v.reason };
257
+ }
258
+ function entryVerdict(name, entry, host, lang) {
259
+ const table = {};
260
+ def(table, name, entry);
261
+ const judged = judgeTable(table, host.env, { expand: true, reserved: new Set(), ...(lang === undefined ? {} : { lang }) });
262
+ const one = judged?.[0];
263
+ if (one === undefined)
264
+ return { ok: false, problems: Object.freeze([]) };
265
+ if (one.spec !== undefined)
266
+ return { ok: true, spec: one.spec, missing: one.missing };
267
+ const nonName = one.problems.filter((p) => !['name_blank', 'name_edge_whitespace', 'name_collides_after_trim', 'name_too_long', 'name_reserved'].includes(p.reason));
268
+ if (one.missing.length > 0 && nonName.length > 0 && nonName.every((p) => p.reason === 'transport_invalid'))
269
+ return { ok: true, spec: undefined, missing: one.missing };
270
+ return { ok: false, problems: nonName.length > 0 ? nonName : one.problems };
271
+ }
272
+ const NAME_N_MAX = 99;
273
+ function policyLockOf(ctx) {
274
+ const ent = ctx.enterprise;
275
+ if (isRecord(ent) && (own(ent, 'status') === 'loaded' || own(ent, 'status') === 'unreadable'))
276
+ return 'enterprise_config';
277
+ const s = ctx.settings;
278
+ if (s === undefined || s === 'none')
279
+ return undefined;
280
+ try {
281
+ const read = readHostSettingsOnce(s, 'applyMcpServerEdit');
282
+ const doc = read.layers.policySettings.doc;
283
+ const v = doc === null ? undefined : own(doc, 'strictPluginOnlyCustomization');
284
+ return v === true || (stringListOf(v) ?? []).includes('mcp') ? 'plugin_only' : undefined;
285
+ }
286
+ catch {
287
+ return undefined;
288
+ }
289
+ }
290
+ function approvalTrust(ctx) {
291
+ const s = ctx.settings;
292
+ if (s === undefined || s === 'none')
293
+ return 'unreadable';
294
+ try {
295
+ return readHostSettingsOnce(s, 'applyMcpServerEdit').trust;
296
+ }
297
+ catch {
298
+ return 'unreadable';
299
+ }
300
+ }
301
+ const targetOf = (e) => JSON.stringify([own(e, 'type') ?? null, own(e, 'command') ?? null, own(e, 'args') ?? null, own(e, 'url') ?? null, own(e, 'headersHelper') ?? null]);
302
+ function maskedArgIndices(args) {
303
+ const list = stringListOf(args);
304
+ if (list === undefined)
305
+ return [];
306
+ const out = [];
307
+ for (const f of settingsCredentialKeyPaths({ args: list })) {
308
+ const m = /^args\[(\d+)\]$/.exec(f.path);
309
+ if (m !== null)
310
+ out.push(Number(m[1]));
311
+ }
312
+ return out;
313
+ }
314
+ function updatedEntry(old, edit, base) {
315
+ const next = copyLayer(old);
316
+ const set = edit.set === undefined ? undefined : edit.set;
317
+ if (set !== undefined)
318
+ for (const k of Object.keys(set))
319
+ def(next, k, own(set, k));
320
+ const unset = stringListOf(edit.unset) ?? [];
321
+ for (const k of unset)
322
+ delete next[k];
323
+ const carried = { env: [], headers: [], args: [] };
324
+ for (const key of ['env', 'headers']) {
325
+ if (unset.includes(key))
326
+ continue;
327
+ const m = edit[key];
328
+ const oldMap = own(old, key);
329
+ if (m === undefined && !isRecord(oldMap))
330
+ continue;
331
+ const map = copyLayer(oldMap);
332
+ const setM = m?.set;
333
+ if (setM !== undefined)
334
+ for (const mk of Object.keys(setM))
335
+ def(map, mk, own(setM, mk));
336
+ for (const mk of stringListOf(m?.delete) ?? [])
337
+ delete map[mk];
338
+ if (isRecord(oldMap))
339
+ for (const mk of Object.keys(oldMap))
340
+ if (Object.hasOwn(map, mk) && (setM === undefined || !Object.hasOwn(setM, mk)))
341
+ carried[key].push(mk);
342
+ if (m !== undefined || isRecord(oldMap))
343
+ def(next, key, map);
344
+ }
345
+ if (!unset.includes('args') && (set === undefined || !Object.hasOwn(set, 'args')))
346
+ carried.args.push(...maskedArgIndices(own(old, 'args')));
347
+ if (targetOf(old) !== targetOf(next) && carried.env.length + carried.headers.length + carried.args.length > 0) {
348
+ const want = [...carried.env.map((n) => `env:${n}`), ...carried.headers.map((n) => `headers:${n}`), ...carried.args.map((i) => `args:${i}`)].sort();
349
+ const keep = edit.keep;
350
+ const got = keep === undefined ? undefined : [...(stringListOf(keep.members) ?? [])].sort();
351
+ if (keep !== undefined && keep.base !== base)
352
+ return { ok: false, detail: { refusal: 'base_conflict' } };
353
+ if (got === undefined || JSON.stringify(got) !== JSON.stringify(want))
354
+ return { ok: false, detail: { refusal: 'credentials_need_reentry', env: Object.freeze(carried.env), headers: Object.freeze(carried.headers), args: Object.freeze(carried.args) } };
355
+ }
356
+ return { ok: true, entry: next };
357
+ }
358
+ function editFactsOf(ctx) {
359
+ const c = isRecord(ctx) ? ctx : { lane: {} };
360
+ const host = isRecord(c.host) ? c.host : {};
361
+ return {
362
+ c,
363
+ host,
364
+ reserved: new Set(stringListOf(own(host, 'reservedNames')) ?? []),
365
+ projectKey: typeof c.projectKey === 'string' && c.projectKey !== '' ? c.projectKey : undefined,
366
+ lang: c.lang,
367
+ };
368
+ }
369
+ function scopeGateOf(scope, f) {
370
+ if (scope === 'local' && f.projectKey === undefined)
371
+ return { refusal: 'scope_not_offered', scope, why: 'no_project_key' };
372
+ if (scope === 'project' && own(f.host, 'projectConfigRootSet') === true)
373
+ return { refusal: 'scope_not_offered', scope, why: 'project_config_root' };
374
+ return undefined;
375
+ }
376
+ function preGatesOf(e, f) {
377
+ if (e.op === 'add' || e.op === 'update' || e.op === 'importFromSibling') {
378
+ const lock = policyLockOf(f.c);
379
+ if (lock !== undefined)
380
+ return { ok: false, detail: { refusal: 'policy_locked', by: lock } };
381
+ const gate = scopeGateOf(e.scope, f);
382
+ if (gate !== undefined)
383
+ return { ok: false, detail: gate };
384
+ }
385
+ if (e.op === 'setEnabled' && f.projectKey === undefined)
386
+ return { ok: false, detail: { refusal: 'scope_not_offered', scope: 'local', why: 'no_project_key' } };
387
+ if (e.op === 'add') {
388
+ const nr = nameRefusal(e.name, [], f.reserved);
389
+ if (nr !== undefined)
390
+ return { ok: false, detail: nr };
391
+ const ev = entryVerdict(e.name, e.entry, f.host, f.lang);
392
+ if (!ev.ok)
393
+ return { ok: false, detail: { refusal: 'mcp_entry_invalid', problems: ev.problems } };
394
+ return { ok: true, entry: { spec: ev.spec, missing: ev.missing } };
395
+ }
396
+ if (e.op === 'importFromSibling') {
397
+ const good = [];
398
+ const skipped = [];
399
+ const problems = [];
400
+ for (const name of Object.keys(e.entries)) {
401
+ const raw = own(e.entries, name);
402
+ const nr = nameRefusal(name, [], f.reserved);
403
+ if (nr !== undefined) {
404
+ skipped.push({ name, reason: nr.refusal });
405
+ continue;
406
+ }
407
+ const ev = entryVerdict(name, raw, f.host, f.lang);
408
+ if (!ev.ok) {
409
+ skipped.push({ name, reason: 'mcp_entry_invalid' });
410
+ problems.push(...ev.problems);
411
+ continue;
412
+ }
413
+ good.push({ name, entry: copyLayer(raw) });
414
+ }
415
+ if (good.length === 0)
416
+ return { ok: false, detail: { refusal: 'mcp_entry_invalid', problems: Object.freeze(problems) }, plan: { op: 'importFromSibling', name: '', scope: e.scope, imported: Object.freeze([]), skipped: Object.freeze(skipped) } };
417
+ return { ok: true, good, skipped };
418
+ }
419
+ return { ok: true };
420
+ }
421
+ const entryProbeOf = (spec) => (spec !== undefined ? { kind: 'spec', spec } : { kind: 'none', why: 'env_missing' });
422
+ function fixedProbeOf(e) {
423
+ if (e.op === 'remove')
424
+ return { kind: 'none', why: 'removed' };
425
+ if (e.op === 'setEnabled')
426
+ return { kind: 'none', why: e.enabled ? 'not_active' : 'removed' };
427
+ return { kind: 'none', why: 'not_active' };
428
+ }
429
+ function updateVerdictOn(r, path, e, f) {
430
+ const cur = documentOf(r);
431
+ const t = subTableOf(cur, path);
432
+ if (t.kind === 'bad')
433
+ return { ok: false, detail: { refusal: 'scope_unreadable' } };
434
+ const old = t.kind === 'table' ? own(t.table, e.name) : undefined;
435
+ if (!isRecord(old))
436
+ return { ok: false, detail: { refusal: 'not_found' } };
437
+ const base = mcpRowBaseOf(r !== undefined && r.status === 'loaded' ? r.revision : null);
438
+ if (base === null || base !== e.base)
439
+ return { ok: false, detail: { refusal: 'base_conflict' } };
440
+ const u = updatedEntry(old, e, base);
441
+ if (!u.ok)
442
+ return { ok: false, detail: u.detail };
443
+ const ev = entryVerdict(e.name, u.entry, f.host, f.lang);
444
+ if (!ev.ok)
445
+ return { ok: false, detail: { refusal: 'mcp_entry_invalid', problems: ev.problems } };
446
+ return { ok: true, cur, table: t.kind === 'table' ? t.table : undefined, entry: u.entry, probe: entryProbeOf(ev.spec) };
447
+ }
448
+ async function snapshotOrUndefined(store) {
449
+ try {
450
+ const raw = await store.read(Object.freeze(['globalConfig', 'projectMcp']));
451
+ const holder = isRecord(raw) ? raw : {};
452
+ return Object.freeze({ globalConfig: fileReadingOf(holder, 'globalConfig', 'unreadable'), projectMcp: fileReadingOf(holder, 'projectMcp', 'unreadable') });
453
+ }
454
+ catch {
455
+ return undefined;
456
+ }
457
+ }
458
+ async function noticesFor(name, scope, entry, store, ctx, missing) {
459
+ const out = [];
460
+ const fresh = await snapshotOrUndefined(store);
461
+ if (fresh !== undefined) {
462
+ const rank = { user: 0, project: 1, local: 2 };
463
+ for (const s of MCP_SERVER_SCOPES) {
464
+ if (s === scope)
465
+ continue;
466
+ const place = placeOf(s, ctx.projectKey);
467
+ if (place === undefined)
468
+ continue;
469
+ const t = subTableOf(docOf(fresh, place.doc), place.path);
470
+ if (t.kind !== 'table')
471
+ continue;
472
+ const other = own(t.table, name);
473
+ if (other === undefined)
474
+ continue;
475
+ if (rank[s] > rank[scope])
476
+ out.push({ kind: 'shadowed_by', scope: s });
477
+ if (JSON.stringify([own(other, 'command') ?? null, own(other, 'url') ?? null]) !== JSON.stringify([own(entry, 'command') ?? null, own(entry, 'url') ?? null]))
478
+ out.push({ kind: 'endpoint_differs', scope: s });
479
+ }
480
+ }
481
+ const dep = ctx.host === undefined ? undefined : stringListOf(own(ctx.host, 'deploymentNames'));
482
+ if (dep !== undefined && dep.includes(name))
483
+ out.push({ kind: 'deployment_wins' });
484
+ if (missing.length > 0)
485
+ out.push({ kind: 'env_missing', names: Object.freeze([...missing]) });
486
+ return out;
487
+ }
488
+ export async function applyMcpServerEdit(edit, store, ctx, opts) {
489
+ try {
490
+ const shaped = shapeMcpServerEdit(edit);
491
+ if (!shaped.ok)
492
+ return refusedOutcome({ refusal: 'edit_malformed', path: shaped.path });
493
+ const e = shaped.edit;
494
+ const f = editFactsOf(ctx);
495
+ const { c, host, reserved, projectKey } = f;
496
+ const dry = isRecord(opts) && opts.dryRun === true;
497
+ const docsFor = () => {
498
+ if (e.op === 'setEnabled')
499
+ return ['globalConfig'];
500
+ if (e.op === 'setProjectApproval')
501
+ return ['localSettings'];
502
+ if (e.op === 'remove' && e.scope === undefined)
503
+ return ['globalConfig', 'projectMcp'];
504
+ if (e.op === 'add' && e.onNameTaken === 'rename')
505
+ return ['globalConfig', 'projectMcp'];
506
+ if (e.op === 'importFromSibling')
507
+ return ['globalConfig', 'projectMcp'];
508
+ return [e.scope === 'project' ? 'projectMcp' : 'globalConfig'];
509
+ };
510
+ const access = localConfigAccess(c.lane, { access: 'write', docs: docsFor() });
511
+ if (!access.ok)
512
+ return Object.freeze({ receipt: Object.freeze({ kind: 'unavailable', cause: access.cause }), notices: Object.freeze([]) });
513
+ const pre = preGatesOf(e, f);
514
+ if (!pre.ok)
515
+ return refusedOutcome(pre.detail, [], pre.plan);
516
+ const box = {};
517
+ const refuse = (d) => {
518
+ box.captured = d;
519
+ return { kind: 'refuse', reason: refusalReasonOf(d.refusal) };
520
+ };
521
+ let step;
522
+ let notices = [];
523
+ if (e.op === 'add') {
524
+ const ev = pre.entry;
525
+ notices = await noticesFor(e.name, e.scope, e.entry, store, c, ev.missing);
526
+ box.probe = entryProbeOf(ev.spec);
527
+ const place = placeOf(e.scope, projectKey);
528
+ const otherDoc = place.doc === 'globalConfig' ? 'projectMcp' : 'globalConfig';
529
+ const mode = e.onNameTaken ?? 'refuse';
530
+ const entryCopy = copyLayer(e.entry);
531
+ step = {
532
+ doc: place.doc,
533
+ role: 'effect',
534
+ ...(mode === 'rename' ? { lock: [otherDoc] } : {}),
535
+ plan: ({ fresh }) => {
536
+ const cur = docOf(fresh, place.doc);
537
+ const t = subTableOf(cur, place.path);
538
+ if (t.kind === 'bad')
539
+ return refuse({ refusal: 'scope_unreadable' });
540
+ const table = t.kind === 'table' ? t.table : undefined;
541
+ const siblings = table === undefined ? [] : Object.keys(table).filter((k) => k !== e.name);
542
+ const sib = nameRefusal(e.name, siblings, reserved);
543
+ if (sib !== undefined)
544
+ return refuse(sib);
545
+ let name = e.name;
546
+ let replaced = false;
547
+ if (table !== undefined && Object.hasOwn(table, e.name)) {
548
+ if (mode === 'refuse')
549
+ return refuse({ refusal: 'name_taken', scope: e.scope, exhausted: false });
550
+ if (mode === 'replace')
551
+ replaced = true;
552
+ else {
553
+ const taken = new Set();
554
+ for (const s of MCP_SERVER_SCOPES) {
555
+ const p = placeOf(s, projectKey);
556
+ if (p === undefined)
557
+ continue;
558
+ const tt = subTableOf(docOf(fresh, p.doc), p.path);
559
+ if (tt.kind === 'table')
560
+ for (const k of Object.keys(tt.table))
561
+ taken.add(k);
562
+ }
563
+ let found;
564
+ for (let n = 1; n <= NAME_N_MAX && found === undefined; n++) {
565
+ const cand = `${e.name}_${n}`;
566
+ if (!taken.has(cand) && nameRefusal(cand, siblings, reserved) === undefined)
567
+ found = cand;
568
+ }
569
+ if (found === undefined)
570
+ return refuse({ refusal: 'name_taken', scope: e.scope, exhausted: true });
571
+ name = found;
572
+ }
573
+ }
574
+ const next = withPath(cur, place.path, tableWith(table, name, entryCopy));
575
+ if (next === undefined)
576
+ return refuse({ refusal: 'scope_unreadable' });
577
+ box.plan = { op: 'add', name, scope: e.scope, ...(name !== e.name ? { renamedFrom: e.name } : {}), ...(replaced ? { replaced: true } : {}) };
578
+ return { kind: 'write', next };
579
+ },
580
+ };
581
+ }
582
+ else if (e.op === 'update') {
583
+ const place = placeOf(e.scope, projectKey);
584
+ step = {
585
+ doc: place.doc,
586
+ role: 'effect',
587
+ plan: ({ fresh }) => {
588
+ const v = updateVerdictOn(fresh[place.doc], place.path, e, f);
589
+ if (!v.ok)
590
+ return refuse(v.detail);
591
+ box.probe = v.probe;
592
+ const next = withPath(v.cur, place.path, tableWith(v.table, e.name, v.entry));
593
+ if (next === undefined)
594
+ return refuse({ refusal: 'scope_unreadable' });
595
+ box.plan = { op: 'update', name: e.name, scope: e.scope };
596
+ return { kind: 'write', next };
597
+ },
598
+ };
599
+ }
600
+ else if (e.op === 'remove') {
601
+ box.probe = fixedProbeOf(e);
602
+ const candidatesIn = (fresh) => {
603
+ const scopes = [];
604
+ let unreadable = false;
605
+ for (const s of ['local', 'project', 'user']) {
606
+ const p = placeOf(s, projectKey);
607
+ if (p === undefined)
608
+ continue;
609
+ const t = subTableOf(docOf(fresh, p.doc), p.path);
610
+ if (t.kind === 'bad')
611
+ unreadable = true;
612
+ else if (t.kind === 'table' && Object.hasOwn(t.table, e.name))
613
+ scopes.push(s);
614
+ }
615
+ return { scopes, unreadable };
616
+ };
617
+ let scope = e.scope;
618
+ if (scope === undefined) {
619
+ const snap = await snapshotOrUndefined(store);
620
+ if (snap === undefined)
621
+ return refusedOutcome({ refusal: 'scope_unreadable' });
622
+ const cand = candidatesIn(snap);
623
+ if (cand.unreadable)
624
+ return refusedOutcome({ refusal: 'scope_unreadable' });
625
+ if (cand.scopes.length === 0)
626
+ return refusedOutcome({ refusal: 'not_found' });
627
+ if (cand.scopes.length > 1)
628
+ return refusedOutcome({ refusal: 'ambiguous_scope', scopes: Object.freeze(cand.scopes) });
629
+ scope = cand.scopes[0];
630
+ }
631
+ const target = scope;
632
+ const gate = scopeGateOf(target, f);
633
+ if (gate !== undefined)
634
+ return refusedOutcome(gate);
635
+ const place = placeOf(target, projectKey);
636
+ const unscoped = e.scope === undefined;
637
+ step = {
638
+ doc: place.doc,
639
+ role: 'effect',
640
+ ...(unscoped ? { lock: [place.doc === 'globalConfig' ? 'projectMcp' : 'globalConfig'] } : {}),
641
+ plan: ({ fresh }) => {
642
+ if (unscoped) {
643
+ const cand = candidatesIn(fresh);
644
+ if (cand.unreadable)
645
+ return refuse({ refusal: 'scope_unreadable' });
646
+ if (cand.scopes.length > 1)
647
+ return refuse({ refusal: 'ambiguous_scope', scopes: Object.freeze(cand.scopes) });
648
+ if (cand.scopes.length === 0)
649
+ return refuse({ refusal: 'not_found' });
650
+ const inLock = scopeGateOf(cand.scopes[0], f);
651
+ if (inLock !== undefined)
652
+ return refuse(inLock);
653
+ if (cand.scopes[0] !== target)
654
+ return refuse({ refusal: 'base_conflict' });
655
+ }
656
+ const cur = docOf(fresh, place.doc);
657
+ const t = subTableOf(cur, place.path);
658
+ if (t.kind === 'bad')
659
+ return refuse({ refusal: 'scope_unreadable' });
660
+ if (t.kind !== 'table' || !Object.hasOwn(t.table, e.name))
661
+ return refuse({ refusal: 'not_found' });
662
+ const next = withPath(cur, place.path, tableWith(t.table, e.name, undefined));
663
+ if (next === undefined)
664
+ return refuse({ refusal: 'scope_unreadable' });
665
+ box.plan = { op: 'remove', name: e.name, scope: target };
666
+ return { kind: 'write', next };
667
+ },
668
+ };
669
+ }
670
+ else if (e.op === 'setEnabled') {
671
+ const defaultOff = new Set(stringListOf(own(host, 'defaultOffNames')) ?? []);
672
+ box.probe = fixedProbeOf(e);
673
+ const projPath = ['projects', projectKey];
674
+ step = {
675
+ doc: 'globalConfig',
676
+ role: 'effect',
677
+ plan: ({ fresh }) => {
678
+ const cur = docOf(fresh, 'globalConfig');
679
+ const t = subTableOf(cur, projPath);
680
+ if (t.kind === 'bad')
681
+ return refuse({ refusal: 'scope_unreadable' });
682
+ const entry = t.kind === 'table' ? t.table : undefined;
683
+ const listAt = (k) => {
684
+ const r = nameListOf(entry, k);
685
+ return r.kind === 'absent' ? [] : r.kind === 'list' ? [...r.list] : undefined;
686
+ };
687
+ const disabled = listAt('disabledMcpServers');
688
+ const enabled = listAt('enabledMcpServers');
689
+ if (disabled === undefined || enabled === undefined)
690
+ return refuse({ refusal: 'scope_unreadable' });
691
+ let nextEntry = copyLayer(entry);
692
+ const put = (k, list, had) => {
693
+ if (list.length === 0 && !had)
694
+ return;
695
+ def(nextEntry, k, list);
696
+ };
697
+ const hadD = entry !== undefined && Object.hasOwn(entry, 'disabledMcpServers');
698
+ const hadE = entry !== undefined && Object.hasOwn(entry, 'enabledMcpServers');
699
+ if (e.enabled) {
700
+ put('disabledMcpServers', disabled.filter((n) => n !== e.name), hadD);
701
+ if (defaultOff.has(e.name))
702
+ put('enabledMcpServers', enabled.includes(e.name) ? enabled : [...enabled, e.name], hadE);
703
+ }
704
+ else {
705
+ put('disabledMcpServers', disabled.includes(e.name) ? disabled : [...disabled, e.name], hadD);
706
+ if (hadE)
707
+ put('enabledMcpServers', enabled.filter((n) => n !== e.name), hadE);
708
+ }
709
+ if (entry === undefined && Object.keys(nextEntry).length === 0)
710
+ nextEntry = {};
711
+ const next = withPath(cur, projPath, nextEntry);
712
+ if (next === undefined)
713
+ return refuse({ refusal: 'scope_unreadable' });
714
+ box.plan = { op: 'setEnabled', name: e.name };
715
+ return { kind: 'write', next };
716
+ },
717
+ };
718
+ }
719
+ else if (e.op === 'setProjectApproval') {
720
+ box.probe = fixedProbeOf(e);
721
+ const WANT = { approve: ['set', 'delete'], reject: ['delete', 'set'], clear: ['delete', 'delete'] };
722
+ const want = WANT[e.approval];
723
+ const edit1 = (intent, op) => (op === 'set' ? { intent, member: e.name, op, value: e.name } : { intent, member: e.name, op });
724
+ const inner = settingsWriteStep({ doc: 'localSettings', trust: approvalTrust(c), edits: [edit1('mcpJsonEnabled', want[0]), edit1('mcpJsonDisabled', want[1])] });
725
+ step = {
726
+ ...inner,
727
+ plan: (input) => {
728
+ const out = inner.plan(input);
729
+ if (out.kind === 'refuse' && out.reason === 'untrusted_folder')
730
+ box.captured = { refusal: 'untrusted_folder' };
731
+ else if (out.kind === 'refuse' && out.reason === 'scope_unwritable')
732
+ box.captured = { refusal: 'scope_not_offered', scope: 'local', why: 'layer_not_admitted' };
733
+ if (out.kind !== 'refuse')
734
+ box.plan = { op: 'setProjectApproval', name: e.name };
735
+ return out;
736
+ },
737
+ };
738
+ }
739
+ else {
740
+ const place = placeOf(e.scope, projectKey);
741
+ const good = pre.good ?? [];
742
+ const skipped = pre.skipped ?? [];
743
+ box.probe = fixedProbeOf(e);
744
+ step = {
745
+ doc: place.doc,
746
+ role: 'effect',
747
+ lock: [place.doc === 'globalConfig' ? 'projectMcp' : 'globalConfig'],
748
+ plan: ({ fresh }) => {
749
+ const cur = docOf(fresh, place.doc);
750
+ const t = subTableOf(cur, place.path);
751
+ if (t.kind === 'bad')
752
+ return refuse({ refusal: 'scope_unreadable' });
753
+ const taken = new Set();
754
+ for (const s of MCP_SERVER_SCOPES) {
755
+ const p = placeOf(s, projectKey);
756
+ if (p === undefined)
757
+ continue;
758
+ const tt = subTableOf(docOf(fresh, p.doc), p.path);
759
+ if (tt.kind === 'table')
760
+ for (const k of Object.keys(tt.table))
761
+ taken.add(k);
762
+ }
763
+ let table = t.kind === 'table' ? copyLayer(t.table) : undefined;
764
+ const imported = [];
765
+ const skippedHere = [...skipped];
766
+ for (const g of good) {
767
+ let name = g.name;
768
+ if (taken.has(name)) {
769
+ let found;
770
+ for (let n = 1; n <= NAME_N_MAX && found === undefined; n++) {
771
+ const cand = `${g.name}_${n}`;
772
+ if (!taken.has(cand) && nameRefusal(cand, [], reserved) === undefined)
773
+ found = cand;
774
+ }
775
+ if (found === undefined) {
776
+ skippedHere.push({ name: g.name, reason: 'name_taken' });
777
+ continue;
778
+ }
779
+ name = found;
780
+ }
781
+ taken.add(name);
782
+ table = tableWith(table, name, g.entry);
783
+ imported.push(name === g.name ? { name } : { name, renamedFrom: g.name });
784
+ }
785
+ if (imported.length === 0)
786
+ return refuse({ refusal: 'name_taken', scope: e.scope, exhausted: true });
787
+ const next = withPath(cur, place.path, table);
788
+ if (next === undefined)
789
+ return refuse({ refusal: 'scope_unreadable' });
790
+ box.plan = { op: 'importFromSibling', name: imported[0].name, scope: e.scope, imported: Object.freeze(imported), skipped: Object.freeze(skippedHere) };
791
+ return { kind: 'write', next };
792
+ },
793
+ };
794
+ }
795
+ const intent = Object.freeze({ appliesOn: 'next_turn', steps: Object.freeze([Object.freeze(step)]) });
796
+ const receipt = dry ? await previewLocalConfigIntent(intent, store, c.lane) : await applyLocalConfigIntent(intent, store, c.lane);
797
+ const captured = box.captured;
798
+ const planOut = box.plan;
799
+ let probe = box.probe;
800
+ const refusal = receipt.kind === 'refused' && captured !== undefined && refusalReasonOf(captured.refusal) === receipt.reason ? captured : undefined;
801
+ if (receipt.kind === 'refused' && probe !== undefined && probe.kind === 'spec')
802
+ probe = { kind: 'none', why: 'edit_refused' };
803
+ return Object.freeze({
804
+ receipt,
805
+ ...(refusal === undefined ? {} : { refusal: Object.freeze(refusal) }),
806
+ ...(planOut === undefined || receipt.kind === 'refused' || receipt.kind === 'unavailable' ? {} : { plan: Object.freeze(planOut) }),
807
+ notices: Object.freeze(notices),
808
+ ...(dry && probe !== undefined ? { probe: Object.freeze(probe) } : {}),
809
+ });
810
+ }
811
+ catch {
812
+ return Object.freeze({ receipt: Object.freeze({ kind: 'refused', reason: 'invalid_request' }), notices: Object.freeze([]) });
813
+ }
814
+ }
815
+ export async function mcpServerProbeTargetOf(edit, store, ctx) {
816
+ const refused = { kind: 'refused' };
817
+ try {
818
+ const f = editFactsOf(ctx);
819
+ const access = localConfigAccess(f.c.lane, { access: 'read' });
820
+ if (!access.ok)
821
+ return Object.freeze({ kind: 'unavailable', cause: access.cause });
822
+ const shaped = shapeMcpServerEdit(edit);
823
+ if (!shaped.ok)
824
+ return refused;
825
+ const e = shaped.edit;
826
+ const pre = preGatesOf(e, f);
827
+ if (!pre.ok)
828
+ return refused;
829
+ if (e.op === 'add')
830
+ return Object.freeze(entryProbeOf(pre.entry?.spec));
831
+ if (e.op === 'update') {
832
+ const place = placeOf(e.scope, f.projectKey);
833
+ const snap = await snapshotOrUndefined(store);
834
+ if (snap === undefined)
835
+ return refused;
836
+ const v = updateVerdictOn(snap[place.doc], place.path, e, f);
837
+ return v.ok ? Object.freeze(v.probe) : refused;
838
+ }
839
+ if (e.op === 'remove' && e.scope !== undefined && scopeGateOf(e.scope, f) !== undefined)
840
+ return refused;
841
+ return Object.freeze(fixedProbeOf(e));
842
+ }
843
+ catch {
844
+ return refused;
845
+ }
846
+ }