@sema-agent/client-core 0.111.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.
- package/CHANGELOG.md +75 -0
- package/README.md +15 -6
- package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
- package/dist/checkpointRowVerdict.d.ts +3 -0
- package/dist/checkpointRowVerdict.js +14 -1
- package/dist/consoleCaPin.d.ts +136 -0
- package/dist/consoleCaPin.js +827 -0
- package/dist/decideReceipt.d.ts +2 -1
- package/dist/deviceRebind.js +3 -3
- package/dist/effectiveFacts.d.ts +9 -0
- package/dist/effectiveFacts.js +33 -0
- package/dist/engineAddress.d.ts +1 -0
- package/dist/engineAddress.js +4 -1
- package/dist/engineCredentialRelease.d.ts +54 -0
- package/dist/engineCredentialRelease.js +146 -0
- package/dist/engineErrorCodes.d.ts +1 -0
- package/dist/engineErrorCodes.js +1 -0
- package/dist/engineHealthFacts.d.ts +0 -11
- package/dist/engineHealthFacts.js +0 -21
- package/dist/engineHealthProbeVerdict.d.ts +1 -0
- package/dist/engineHealthProbeVerdict.js +21 -13
- package/dist/engineUnavailable.d.ts +3 -2
- package/dist/engineUnavailable.js +16 -12
- package/dist/engineUrl.js +4 -2
- package/dist/gateFailureCodes.js +2 -2
- package/dist/generated/sdkFactTables.d.ts +1 -0
- package/dist/generated/sdkFactTables.js +13 -0
- package/dist/generated/settingsSchemaFns.d.ts +1 -0
- package/dist/generated/settingsSchemaFns.js +3 -0
- package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
- package/dist/generated/settingsSchemaTables.d.ts +52 -0
- package/dist/generated/settingsSchemaTables.js +18 -0
- package/dist/generated/settingsSchemaTypes.d.ts +23 -13
- package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
- package/dist/generated/settingsSchemaUrlShape.js +17 -0
- package/dist/hitl/decisionRefusal.d.ts +4 -0
- package/dist/hitl/decisionRefusal.js +12 -2
- package/dist/hitl/localAllowRedemption.d.ts +9 -6
- package/dist/hitl/localAllowRedemption.js +20 -0
- package/dist/hitl/parkOwnership.js +2 -1
- package/dist/hitl/parkResolver.js +7 -0
- package/dist/hitl/screenDecisionCards.js +3 -2
- package/dist/hitl/sessionPolicyWire.js +2 -2
- package/dist/hitl/standingRows.d.ts +4 -2
- package/dist/hitl/standingRows.js +10 -3
- package/dist/hooksReaderGenerations.js +1 -1
- package/dist/i18n/messages.en.d.ts +163 -0
- package/dist/i18n/messages.en.js +163 -0
- package/dist/i18n/messages.zh.js +174 -0
- package/dist/index.d.ts +30 -5
- package/dist/index.js +15 -2
- package/dist/liveModelCatalog.d.ts +4 -0
- package/dist/liveModelCatalog.js +10 -1
- package/dist/localConfig/documents.d.ts +61 -0
- package/dist/localConfig/documents.js +51 -0
- package/dist/localConfig/kernel.d.ts +149 -0
- package/dist/localConfig/kernel.js +597 -0
- package/dist/localConfig/mcp/edit.d.ts +161 -0
- package/dist/localConfig/mcp/edit.js +846 -0
- package/dist/localConfig/mcp/servers.d.ts +165 -0
- package/dist/localConfig/mcp/servers.js +776 -0
- package/dist/localConfig/mcp/views.d.ts +170 -0
- package/dist/localConfig/mcp/views.js +429 -0
- package/dist/localConfig/modelPool.d.ts +262 -0
- package/dist/localConfig/modelPool.js +1489 -0
- package/dist/localConfig/modelPoolWords.d.ts +13 -0
- package/dist/localConfig/modelPoolWords.js +20 -0
- package/dist/localConfig/receiptShape.d.ts +7 -0
- package/dist/localConfig/receiptShape.js +135 -0
- package/dist/localConfig/settingsWrite.d.ts +127 -0
- package/dist/localConfig/settingsWrite.js +237 -0
- package/dist/localConfig/skills.d.ts +124 -0
- package/dist/localConfig/skills.js +397 -0
- package/dist/localConfig/skillsWords.d.ts +10 -0
- package/dist/localConfig/skillsWords.js +5 -0
- package/dist/mcpProbeWire.js +2 -1
- package/dist/mcpWireCaps.js +1 -1
- package/dist/memoryVerbsWire.js +3 -2
- package/dist/model/onboardJudgement.d.ts +14 -4
- package/dist/model/onboardJudgement.js +91 -139
- package/dist/modelConfigGapNotice.d.ts +20 -0
- package/dist/modelConfigGapNotice.js +102 -0
- package/dist/modsKernelIdentity.js +2 -47
- package/dist/ownKey.d.ts +1 -0
- package/dist/ownKey.js +40 -8
- package/dist/permissionsDeclaration.d.ts +2 -1
- package/dist/permissionsDeclaration.js +31 -17
- package/dist/promptBlockedCopy.js +2 -2
- package/dist/resumeRefusalCopy.d.ts +3 -1
- package/dist/resumeRefusalCopy.js +12 -4
- package/dist/runLedgerSnapshot.js +3 -3
- package/dist/runPermissionModeSwitch.js +2 -3
- package/dist/seatContract.d.ts +154 -4
- package/dist/seatContract.js +228 -2
- package/dist/sessionIdSegment.d.ts +1 -0
- package/dist/sessionIdSegment.js +4 -1
- package/dist/sessionMemoryErase.js +2 -1
- package/dist/sha256Bytes.d.ts +1 -0
- package/dist/sha256Bytes.js +46 -0
- package/dist/skillOverrideChain.d.ts +83 -0
- package/dist/skillOverrideChain.js +378 -0
- package/dist/skillsWireCaps.d.ts +3 -0
- package/dist/skillsWireCaps.js +16 -1
- package/dist/turnFailurePresentation.js +4 -10
- package/docs/INTEGRATION-CLIENTS.md +614 -14
- package/package.json +3 -3
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
import { LOCAL_CONFIG_WRITE_INTENTS } from '../settingsWrite.js';
|
|
2
|
+
import { projectMcpConfigs, mcpTransportVerdict } from '../../mcpWireCaps.js';
|
|
3
|
+
import { readHostSettingsOnce, CC_MERGE_ORDER } from '../../hostSettingsSource.js';
|
|
4
|
+
import { layerAdmissionWord } from '../../settingsLayerAdmission.js';
|
|
5
|
+
import { msg, renderMessage } from '../../i18n/translate.js';
|
|
6
|
+
export const MCP_SERVER_SCOPES = Object.freeze(['user', 'project', 'local']);
|
|
7
|
+
export const MCP_SERVER_ROW_STATES = Object.freeze(['active', 'pending_approval', 'rejected', 'disabled', 'withheld', 'unusable']);
|
|
8
|
+
export const MCP_SERVER_WITHHELD_CAUSES = Object.freeze(['narrowing_conflict', 'narrowing_invalid', 'layer_unreadable', 'policy_locked']);
|
|
9
|
+
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
10
|
+
export function ownData(v, key) {
|
|
11
|
+
try {
|
|
12
|
+
if (typeof v !== 'object' || v === null)
|
|
13
|
+
return { ok: true, value: undefined };
|
|
14
|
+
const d = Object.getOwnPropertyDescriptor(v, key);
|
|
15
|
+
if (d === undefined)
|
|
16
|
+
return { ok: true, value: undefined };
|
|
17
|
+
return Object.hasOwn(d, 'value') ? { ok: true, value: d.value } : { ok: false };
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return { ok: false };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const ownValue = (v, key) => {
|
|
24
|
+
const r = ownData(v, key);
|
|
25
|
+
return r.ok ? r.value : undefined;
|
|
26
|
+
};
|
|
27
|
+
export function stringListOf(v) {
|
|
28
|
+
try {
|
|
29
|
+
if (!Array.isArray(v) || v.length > 100_000)
|
|
30
|
+
return undefined;
|
|
31
|
+
const out = [];
|
|
32
|
+
for (let i = 0; i < v.length; i++) {
|
|
33
|
+
const r = ownData(v, String(i));
|
|
34
|
+
if (!r.ok || typeof r.value !== 'string')
|
|
35
|
+
return undefined;
|
|
36
|
+
out.push(r.value);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function nameListOf(holder, key) {
|
|
45
|
+
const r = ownData(holder, key);
|
|
46
|
+
if (!r.ok)
|
|
47
|
+
return { kind: 'bad' };
|
|
48
|
+
if (r.value === undefined)
|
|
49
|
+
return { kind: 'absent' };
|
|
50
|
+
const list = stringListOf(r.value);
|
|
51
|
+
return list === undefined ? { kind: 'bad' } : { kind: 'list', list: Object.freeze(list) };
|
|
52
|
+
}
|
|
53
|
+
const grantedNames = (r) => new Set(r.kind === 'list' ? r.list : []);
|
|
54
|
+
function ownArrayLength(v) {
|
|
55
|
+
try {
|
|
56
|
+
if (!Array.isArray(v))
|
|
57
|
+
return undefined;
|
|
58
|
+
const d = Object.getOwnPropertyDescriptor(v, 'length');
|
|
59
|
+
return d !== undefined && typeof d.value === 'number' && Number.isSafeInteger(d.value) && d.value >= 0 && d.value <= 100_000 ? d.value : undefined;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function pureJsonOf(v, depth, path) {
|
|
66
|
+
try {
|
|
67
|
+
if (v === null || typeof v === 'string' || typeof v === 'boolean')
|
|
68
|
+
return JSON.stringify(v);
|
|
69
|
+
if (typeof v === 'number')
|
|
70
|
+
return Number.isFinite(v) ? JSON.stringify(v) : undefined;
|
|
71
|
+
if (typeof v !== 'object' || depth > 32 || path.has(v))
|
|
72
|
+
return undefined;
|
|
73
|
+
const arr = Array.isArray(v);
|
|
74
|
+
const proto = Object.getPrototypeOf(v);
|
|
75
|
+
if (arr ? proto !== Array.prototype : proto !== Object.prototype && proto !== null)
|
|
76
|
+
return undefined;
|
|
77
|
+
path.add(v);
|
|
78
|
+
try {
|
|
79
|
+
const parts = [];
|
|
80
|
+
const keys = Reflect.ownKeys(v);
|
|
81
|
+
if (arr) {
|
|
82
|
+
const len = ownArrayLength(v);
|
|
83
|
+
if (len === undefined || keys.length !== len + 1)
|
|
84
|
+
return undefined;
|
|
85
|
+
for (let i = 0; i < len; i++) {
|
|
86
|
+
const d = Object.getOwnPropertyDescriptor(v, String(i));
|
|
87
|
+
if (d === undefined || !Object.hasOwn(d, 'value'))
|
|
88
|
+
return undefined;
|
|
89
|
+
const s = pureJsonOf(d.value, depth + 1, path);
|
|
90
|
+
if (s === undefined)
|
|
91
|
+
return undefined;
|
|
92
|
+
parts.push(s);
|
|
93
|
+
}
|
|
94
|
+
return `[${parts.join(',')}]`;
|
|
95
|
+
}
|
|
96
|
+
for (const k of keys) {
|
|
97
|
+
if (typeof k !== 'string')
|
|
98
|
+
return undefined;
|
|
99
|
+
const d = Object.getOwnPropertyDescriptor(v, k);
|
|
100
|
+
if (d === undefined)
|
|
101
|
+
continue;
|
|
102
|
+
if (!Object.hasOwn(d, 'value') || d.enumerable !== true)
|
|
103
|
+
return undefined;
|
|
104
|
+
const s = pureJsonOf(d.value, depth + 1, path);
|
|
105
|
+
if (s === undefined)
|
|
106
|
+
return undefined;
|
|
107
|
+
parts.push(`${JSON.stringify(k)}:${s}`);
|
|
108
|
+
}
|
|
109
|
+
return `{${parts.join(',')}}`;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
path.delete(v);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function envSignatureOf(entry) {
|
|
120
|
+
const r = ownData(entry, 'env');
|
|
121
|
+
if (!r.ok)
|
|
122
|
+
return undefined;
|
|
123
|
+
return r.value === undefined ? 'null' : pureJsonOf(r.value, 0, new Set());
|
|
124
|
+
}
|
|
125
|
+
function ownKeysOf(v) {
|
|
126
|
+
try {
|
|
127
|
+
return Object.keys(v);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function subTableOf(doc, path) {
|
|
134
|
+
let box = doc;
|
|
135
|
+
for (const seg of path) {
|
|
136
|
+
if (!isRecord(box))
|
|
137
|
+
return { kind: 'bad' };
|
|
138
|
+
const r = ownData(box, seg);
|
|
139
|
+
if (!r.ok)
|
|
140
|
+
return { kind: 'bad' };
|
|
141
|
+
if (r.value === undefined)
|
|
142
|
+
return { kind: 'absent' };
|
|
143
|
+
box = r.value;
|
|
144
|
+
}
|
|
145
|
+
return isRecord(box) ? { kind: 'table', table: box } : { kind: 'bad' };
|
|
146
|
+
}
|
|
147
|
+
export function documentOf(r) {
|
|
148
|
+
if (r === undefined)
|
|
149
|
+
return undefined;
|
|
150
|
+
if (r.status === 'loaded')
|
|
151
|
+
return r.doc;
|
|
152
|
+
return r.status === 'absent' ? {} : undefined;
|
|
153
|
+
}
|
|
154
|
+
export const localScopePath = (projectKey) => Object.freeze(['projects', projectKey, 'mcpServers']);
|
|
155
|
+
export const USER_SCOPE_PATH = Object.freeze(['mcpServers']);
|
|
156
|
+
export const PROJECT_FILE_PATH = Object.freeze(['mcpServers']);
|
|
157
|
+
function splitPath(p, sep) {
|
|
158
|
+
if (sep === '/') {
|
|
159
|
+
if (!p.startsWith('/'))
|
|
160
|
+
return undefined;
|
|
161
|
+
return { root: '/', parts: p.split('/').filter((s) => s !== '') };
|
|
162
|
+
}
|
|
163
|
+
const m = /^([A-Za-z]:)\\/.exec(p);
|
|
164
|
+
if (m === null)
|
|
165
|
+
return undefined;
|
|
166
|
+
return { root: `${m[1]}\\`, parts: p.slice(m[0].length).split('\\').filter((s) => s !== '') };
|
|
167
|
+
}
|
|
168
|
+
const joinPath = (root, parts, sep) => root + parts.join(sep);
|
|
169
|
+
export function mcpProjectChainOf(startDir, sep, fence) {
|
|
170
|
+
try {
|
|
171
|
+
if (typeof startDir !== 'string' || (sep !== '/' && sep !== '\\'))
|
|
172
|
+
return Object.freeze([]);
|
|
173
|
+
const s = splitPath(startDir, sep);
|
|
174
|
+
if (s === undefined || s.parts.length === 0)
|
|
175
|
+
return Object.freeze([]);
|
|
176
|
+
let floor = 1;
|
|
177
|
+
if (fence !== undefined) {
|
|
178
|
+
if (typeof fence !== 'string')
|
|
179
|
+
return Object.freeze([]);
|
|
180
|
+
const f = splitPath(fence, sep);
|
|
181
|
+
if (f === undefined || f.root !== s.root || f.parts.length > s.parts.length || f.parts.some((x, i) => s.parts[i] !== x))
|
|
182
|
+
return Object.freeze([]);
|
|
183
|
+
floor = Math.max(1, f.parts.length);
|
|
184
|
+
}
|
|
185
|
+
const out = [];
|
|
186
|
+
for (let n = floor; n <= s.parts.length; n++)
|
|
187
|
+
out.push(joinPath(s.root, s.parts.slice(0, n), sep));
|
|
188
|
+
return Object.freeze(out);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return Object.freeze([]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export function mcpPathWithinFence(physicalPath, fence, sep) {
|
|
195
|
+
try {
|
|
196
|
+
if (typeof physicalPath !== 'string' || typeof fence !== 'string')
|
|
197
|
+
return false;
|
|
198
|
+
const p = splitPath(physicalPath, sep);
|
|
199
|
+
const f = splitPath(fence, sep);
|
|
200
|
+
if (p === undefined || f === undefined || p.root !== f.root || f.parts.length > p.parts.length)
|
|
201
|
+
return false;
|
|
202
|
+
return f.parts.every((x, i) => p.parts[i] === x);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const REMOTE_SINK_WITHHELD_NAMES = new Set([
|
|
209
|
+
'AWS_ACCESS_KEY_ID',
|
|
210
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
211
|
+
'AWS_SESSION_TOKEN',
|
|
212
|
+
'AZURE_CLIENT_SECRET',
|
|
213
|
+
'GOOGLE_APPLICATION_CREDENTIALS',
|
|
214
|
+
'GITHUB_TOKEN',
|
|
215
|
+
'GH_TOKEN',
|
|
216
|
+
'GITLAB_TOKEN',
|
|
217
|
+
'NPM_TOKEN',
|
|
218
|
+
'NODE_AUTH_TOKEN',
|
|
219
|
+
'PYPI_TOKEN',
|
|
220
|
+
'ANTHROPIC_API_KEY',
|
|
221
|
+
'OPENAI_API_KEY',
|
|
222
|
+
]);
|
|
223
|
+
const VAR_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g;
|
|
224
|
+
export function expandMcpServerEntry(entry, env, target) {
|
|
225
|
+
const missing = [];
|
|
226
|
+
const withheld = [];
|
|
227
|
+
try {
|
|
228
|
+
if (!isRecord(entry))
|
|
229
|
+
return Object.freeze({ entry, missing: Object.freeze([]), withheld: Object.freeze([]) });
|
|
230
|
+
const values = env !== undefined && isRecord(env) ? ownValue(env, 'values') : undefined;
|
|
231
|
+
const secrets = new Set(stringListOf(env !== undefined && isRecord(env) ? ownValue(env, 'hostSecretNames') : undefined) ?? []);
|
|
232
|
+
const x = (s) => s.replace(VAR_RE, (whole, name, def) => {
|
|
233
|
+
if (secrets.has(name) || (target === 'remote' && REMOTE_SINK_WITHHELD_NAMES.has(name))) {
|
|
234
|
+
if (!withheld.includes(name))
|
|
235
|
+
withheld.push(name);
|
|
236
|
+
return def ?? '';
|
|
237
|
+
}
|
|
238
|
+
const v = isRecord(values) ? ownValue(values, name) : undefined;
|
|
239
|
+
if (typeof v === 'string')
|
|
240
|
+
return v;
|
|
241
|
+
if (def !== undefined)
|
|
242
|
+
return def;
|
|
243
|
+
if (!missing.includes(name))
|
|
244
|
+
missing.push(name);
|
|
245
|
+
return whole;
|
|
246
|
+
});
|
|
247
|
+
const out = {};
|
|
248
|
+
const keys = ownKeysOf(entry) ?? [];
|
|
249
|
+
for (const k of keys) {
|
|
250
|
+
const r = ownData(entry, k);
|
|
251
|
+
if (!r.ok) {
|
|
252
|
+
const d = Object.getOwnPropertyDescriptor(entry, k);
|
|
253
|
+
if (d !== undefined)
|
|
254
|
+
Object.defineProperty(out, k, d);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
let v = r.value;
|
|
258
|
+
if ((k === 'command' || k === 'url') && typeof v === 'string')
|
|
259
|
+
v = x(v);
|
|
260
|
+
else if (k === 'args') {
|
|
261
|
+
const list = stringListOf(v);
|
|
262
|
+
if (list !== undefined)
|
|
263
|
+
v = list.map(x);
|
|
264
|
+
}
|
|
265
|
+
else if ((k === 'env' || k === 'headers') && isRecord(v)) {
|
|
266
|
+
const src = v;
|
|
267
|
+
const map = {};
|
|
268
|
+
let ok = true;
|
|
269
|
+
for (const mk of ownKeysOf(src) ?? []) {
|
|
270
|
+
const mr = ownData(src, mk);
|
|
271
|
+
if (!mr.ok) {
|
|
272
|
+
ok = false;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
Object.defineProperty(map, mk, { value: typeof mr.value === 'string' ? x(mr.value) : mr.value, writable: true, enumerable: true, configurable: true });
|
|
276
|
+
}
|
|
277
|
+
if (ok)
|
|
278
|
+
v = map;
|
|
279
|
+
}
|
|
280
|
+
Object.defineProperty(out, k, { value: v, writable: true, enumerable: true, configurable: true });
|
|
281
|
+
}
|
|
282
|
+
return Object.freeze({ entry: out, missing: Object.freeze([...missing]), withheld: Object.freeze([...withheld]) });
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return Object.freeze({ entry, missing: Object.freeze([...missing]), withheld: Object.freeze([...withheld]) });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
export function expansionTargetOf(entry) {
|
|
289
|
+
const t = ownValue(entry, 'type');
|
|
290
|
+
if (typeof t === 'string')
|
|
291
|
+
return t === 'stdio' ? 'plain' : 'remote';
|
|
292
|
+
return typeof ownValue(entry, 'url') === 'string' && ownValue(entry, 'command') === undefined ? 'remote' : 'plain';
|
|
293
|
+
}
|
|
294
|
+
const NAME_REASONS = Object.freeze({ name_blank: true, name_edge_whitespace: true, name_collides_after_trim: true, name_too_long: true, name_reserved: true });
|
|
295
|
+
export function judgeTable(table, env, opts) {
|
|
296
|
+
try {
|
|
297
|
+
if (!isRecord(table))
|
|
298
|
+
return undefined;
|
|
299
|
+
const names = ownKeysOf(table);
|
|
300
|
+
if (names === undefined)
|
|
301
|
+
return undefined;
|
|
302
|
+
const forJudge = {};
|
|
303
|
+
const pre = [];
|
|
304
|
+
for (const name of names) {
|
|
305
|
+
const r = ownData(table, name);
|
|
306
|
+
const raw = r.ok ? r.value : undefined;
|
|
307
|
+
const reserved = opts.reserved.has(name);
|
|
308
|
+
const envSig = opts.signature === true && r.ok ? envSignatureOf(raw) : undefined;
|
|
309
|
+
const readable = r.ok && (opts.signature !== true || envSig !== undefined);
|
|
310
|
+
const exp = readable && !reserved ? (opts.expand ? expandMcpServerEntry(raw, env, expansionTargetOf(raw)) : { entry: raw, missing: [], withheld: [] }) : undefined;
|
|
311
|
+
pre.push({ name, raw, readable, exp, reserved, envSig });
|
|
312
|
+
if (!reserved && readable)
|
|
313
|
+
Object.defineProperty(forJudge, name, { value: exp?.entry, writable: true, enumerable: true, configurable: true });
|
|
314
|
+
else if (!reserved)
|
|
315
|
+
Object.defineProperty(forJudge, name, {
|
|
316
|
+
get() {
|
|
317
|
+
throw new Error('entry unreadable');
|
|
318
|
+
},
|
|
319
|
+
enumerable: true,
|
|
320
|
+
configurable: true,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
const projected = projectMcpConfigs(forJudge, opts.lang === undefined ? undefined : { lang: opts.lang });
|
|
324
|
+
const out = [];
|
|
325
|
+
for (const p of pre) {
|
|
326
|
+
if (p.reserved) {
|
|
327
|
+
out.push({ name: p.name, raw: p.raw, spec: undefined, problems: Object.freeze([{ reason: 'name_reserved', notice: renderMessage(msg('config.mcpServers.problem.nameReserved'), opts.lang) }]), missing: [], withheld: [], vote: { kind: 'none' }, reserved: true, ...(p.envSig === undefined ? {} : { envSig: p.envSig }) });
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const spec = projected.specs.find((s) => s.name === p.name);
|
|
331
|
+
const drops = projected.dropped.filter((d) => d.name === p.name);
|
|
332
|
+
const problems = drops.map((d) => Object.freeze({ reason: d.reason, ...(d.path === undefined ? {} : { path: d.path }), notice: d.notice }));
|
|
333
|
+
if (spec === undefined && problems.length === 0) {
|
|
334
|
+
const tv = isRecord(p.exp?.entry) ? mcpTransportVerdict(p.exp?.entry) : undefined;
|
|
335
|
+
problems.push(Object.freeze({ reason: 'transport_invalid', path: tv !== undefined && tv.kind === 'bad' ? tv.key : 'transport.kind', notice: tv !== undefined && tv.kind === 'bad' ? tv.detail : 'transport not supported' }));
|
|
336
|
+
}
|
|
337
|
+
const nameBad = drops.some((d) => Object.hasOwn(NAME_REASONS, d.reason));
|
|
338
|
+
const notObject = !p.readable || !isRecord(p.raw);
|
|
339
|
+
let vote;
|
|
340
|
+
if (nameBad || notObject)
|
|
341
|
+
vote = { kind: 'none' };
|
|
342
|
+
else {
|
|
343
|
+
const a = ownData(p.raw, 'allowTools');
|
|
344
|
+
const transportBad = spec === undefined && problems.some((x) => x.reason === 'transport_invalid' || x.reason === 'identity_headers_both_set');
|
|
345
|
+
if (!a.ok)
|
|
346
|
+
vote = { kind: 'bad' };
|
|
347
|
+
else if (a.value === undefined)
|
|
348
|
+
vote = { kind: 'absent' };
|
|
349
|
+
else if (transportBad)
|
|
350
|
+
vote = { kind: 'bad' };
|
|
351
|
+
else {
|
|
352
|
+
const list = stringListOf(a.value);
|
|
353
|
+
vote = list === undefined ? { kind: 'bad' } : { kind: 'list', list: Object.freeze(list) };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
out.push({ name: p.name, raw: p.raw, spec, problems: Object.freeze(problems), missing: p.exp?.missing ?? [], withheld: p.exp?.withheld ?? [], vote, reserved: false, ...(p.envSig === undefined ? {} : { envSig: p.envSig }) });
|
|
357
|
+
}
|
|
358
|
+
return Object.freeze(out);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const LOOSEN_LAYERS = CC_MERGE_ORDER;
|
|
365
|
+
function approvalCategoryOf() {
|
|
366
|
+
const row = LOCAL_CONFIG_WRITE_INTENTS.find((r) => r.intent === 'mcpJsonEnabled');
|
|
367
|
+
return row === undefined ? '' : row.admitVia;
|
|
368
|
+
}
|
|
369
|
+
function approvalFactsOf(read, facts, purpose) {
|
|
370
|
+
const session = grantedNames(nameListOf(facts, 'sessionApproved'));
|
|
371
|
+
if (read === undefined)
|
|
372
|
+
return { rejected: new Set(), rejectedUnreadable: false, approvedBySettings: () => false, session, mayApprove: session.size > 0, autoBy: undefined, withheld: Object.freeze([]) };
|
|
373
|
+
const rejected = new Set();
|
|
374
|
+
let rejectedUnreadable = false;
|
|
375
|
+
const admitted = [];
|
|
376
|
+
const withheld = [];
|
|
377
|
+
for (const layer of LOOSEN_LAYERS) {
|
|
378
|
+
const snap = read.layers[layer];
|
|
379
|
+
if (!snap.enabled || snap.doc === null)
|
|
380
|
+
continue;
|
|
381
|
+
const rej = nameListOf(snap.doc, 'disabledMcpjsonServers');
|
|
382
|
+
if (rej.kind === 'bad')
|
|
383
|
+
rejectedUnreadable = true;
|
|
384
|
+
else if (rej.kind === 'list')
|
|
385
|
+
for (const n of rej.list)
|
|
386
|
+
rejected.add(n);
|
|
387
|
+
let word;
|
|
388
|
+
try {
|
|
389
|
+
word = layerAdmissionWord(layer, read.trust, approvalCategoryOf());
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
word = 'unknown';
|
|
393
|
+
}
|
|
394
|
+
if (word === 'admitted')
|
|
395
|
+
admitted.push(layer);
|
|
396
|
+
else if (word === 'trust_withheld' && (layer === 'projectSettings' || layer === 'localSettings')) {
|
|
397
|
+
const all = ownValue(snap.doc, 'enableAllProjectMcpServers') === true;
|
|
398
|
+
const en = nameListOf(snap.doc, 'enabledMcpjsonServers');
|
|
399
|
+
const named = en.kind === 'bad' || (en.kind === 'list' && en.list.length > 0);
|
|
400
|
+
if (all || named)
|
|
401
|
+
withheld.push({ layer, cause: 'trust_withheld' });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const lists = admitted.map((l) => grantedNames(nameListOf(read.layers[l].doc, 'enabledMcpjsonServers')));
|
|
405
|
+
const union = new Set(lists.flatMap((s) => [...s]));
|
|
406
|
+
let approvedBySettings;
|
|
407
|
+
let mayApprove;
|
|
408
|
+
if (read.trust === 'accepted') {
|
|
409
|
+
let all;
|
|
410
|
+
for (const l of admitted) {
|
|
411
|
+
const v = ownValue(read.layers[l].doc, 'enableAllProjectMcpServers');
|
|
412
|
+
if (typeof v === 'boolean')
|
|
413
|
+
all = v;
|
|
414
|
+
}
|
|
415
|
+
approvedBySettings = (name) => all === true || union.has(name);
|
|
416
|
+
mayApprove = all === true || union.size > 0;
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
const anyAll = admitted.some((l) => ownValue(read.layers[l].doc, 'enableAllProjectMcpServers') === true);
|
|
420
|
+
approvedBySettings = (name) => anyAll || union.has(name);
|
|
421
|
+
mayApprove = anyAll || union.size > 0;
|
|
422
|
+
}
|
|
423
|
+
let autoBy;
|
|
424
|
+
if (purpose === 'load' && read.layers.projectSettings.enabled) {
|
|
425
|
+
const skip = ['policySettings', 'userSettings'].some((l) => read.layers[l].doc !== null && ownValue(read.layers[l].doc, 'skipDangerousModePermissionPrompt') === true);
|
|
426
|
+
if (ownValue(facts, 'permissionMode') === 'bypass' && skip)
|
|
427
|
+
autoBy = 'bypass';
|
|
428
|
+
else if (ownValue(facts, 'nonInteractive') === true)
|
|
429
|
+
autoBy = 'non_interactive';
|
|
430
|
+
}
|
|
431
|
+
return { rejected, rejectedUnreadable, approvedBySettings, session, mayApprove: mayApprove || session.size > 0 || autoBy !== undefined, autoBy, withheld: Object.freeze(withheld) };
|
|
432
|
+
}
|
|
433
|
+
function approvalOf(a, name) {
|
|
434
|
+
if (a.rejected.has(name))
|
|
435
|
+
return { state: 'rejected' };
|
|
436
|
+
if (a.approvedBySettings(name))
|
|
437
|
+
return { state: 'approved', by: 'settings' };
|
|
438
|
+
if (a.session.has(name))
|
|
439
|
+
return { state: 'approved', by: 'session' };
|
|
440
|
+
if (a.autoBy !== undefined)
|
|
441
|
+
return { state: 'approved', by: a.autoBy };
|
|
442
|
+
return { state: 'pending' };
|
|
443
|
+
}
|
|
444
|
+
export function fileReadingOf(holder, key, missing) {
|
|
445
|
+
const slot = ownData(holder, key);
|
|
446
|
+
if (!slot.ok)
|
|
447
|
+
return { status: 'unreadable', cause: 'io' };
|
|
448
|
+
const r = slot.value;
|
|
449
|
+
if (r === undefined)
|
|
450
|
+
return missing === 'absent' ? { status: 'absent' } : { status: 'unreadable', cause: 'io' };
|
|
451
|
+
if (!isRecord(r))
|
|
452
|
+
return { status: 'unreadable', cause: 'io' };
|
|
453
|
+
const status = ownData(r, 'status');
|
|
454
|
+
if (!status.ok)
|
|
455
|
+
return { status: 'unreadable', cause: 'io' };
|
|
456
|
+
if (status.value === 'absent')
|
|
457
|
+
return { status: 'absent' };
|
|
458
|
+
if (status.value === 'outside_fence')
|
|
459
|
+
return { status: 'outside_fence' };
|
|
460
|
+
if (status.value !== 'loaded')
|
|
461
|
+
return { status: 'unreadable', cause: 'io' };
|
|
462
|
+
const doc = ownData(r, 'doc');
|
|
463
|
+
const revision = ownData(r, 'revision');
|
|
464
|
+
if (!doc.ok || !isRecord(doc.value) || !revision.ok || typeof revision.value !== 'string' || revision.value === '')
|
|
465
|
+
return { status: 'unreadable', cause: 'io' };
|
|
466
|
+
const loaded = { status: 'loaded', doc: doc.value, revision: revision.value };
|
|
467
|
+
return Object.freeze(loaded);
|
|
468
|
+
}
|
|
469
|
+
const revisionOf = (r) => (r.status === 'loaded' ? r.revision : null);
|
|
470
|
+
const sourceTableOf = (r, path) => (r.status === 'outside_fence' ? { kind: 'absent' } : subTableOf(documentOf(r), path));
|
|
471
|
+
function signatureOf(entry) {
|
|
472
|
+
const url = ownValue(entry, 'url');
|
|
473
|
+
if (typeof url === 'string')
|
|
474
|
+
return `u:${url}`;
|
|
475
|
+
const cmd = ownValue(entry, 'command');
|
|
476
|
+
if (typeof cmd !== 'string')
|
|
477
|
+
return undefined;
|
|
478
|
+
return `c:${JSON.stringify([cmd, stringListOf(ownValue(entry, 'args')) ?? []])}`;
|
|
479
|
+
}
|
|
480
|
+
export function mergeMcpServers(input, purpose) {
|
|
481
|
+
const lang = input.lang;
|
|
482
|
+
const facts = isRecord(input.facts) ? input.facts : {};
|
|
483
|
+
const files = isRecord(input.files) ? input.files : {};
|
|
484
|
+
const supplementary = isRecord(input.supplementarySources) ? input.supplementarySources : {};
|
|
485
|
+
let read;
|
|
486
|
+
if (input.settings !== 'none') {
|
|
487
|
+
try {
|
|
488
|
+
read = readHostSettingsOnce(input.settings, 'mcpServersReport');
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
read = undefined;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const env = ownValue(facts, 'env');
|
|
495
|
+
const reserved = new Set(stringListOf(ownValue(facts, 'reservedNames')) ?? []);
|
|
496
|
+
const none = new Set();
|
|
497
|
+
const approvals = approvalFactsOf(read, facts, purpose);
|
|
498
|
+
const unreadable = new Set();
|
|
499
|
+
const g = fileReadingOf(files, 'global', 'absent');
|
|
500
|
+
const ent = fileReadingOf(files, 'enterprise', 'absent');
|
|
501
|
+
const projectKeyRaw = ownValue(files, 'projectKey');
|
|
502
|
+
const projectKey = typeof projectKeyRaw === 'string' && projectKeyRaw !== '' ? projectKeyRaw : undefined;
|
|
503
|
+
const chain = [];
|
|
504
|
+
const chainSlot = ownData(files, 'projectChain');
|
|
505
|
+
if (!chainSlot.ok)
|
|
506
|
+
chain.push({ status: 'unreadable', cause: 'io' });
|
|
507
|
+
else if (chainSlot.value !== undefined) {
|
|
508
|
+
const len = ownArrayLength(chainSlot.value);
|
|
509
|
+
if (len === undefined)
|
|
510
|
+
chain.push({ status: 'unreadable', cause: 'io' });
|
|
511
|
+
else
|
|
512
|
+
for (let i = 0; i < len; i++)
|
|
513
|
+
chain.push(fileReadingOf(chainSlot.value, String(i), 'unreadable'));
|
|
514
|
+
}
|
|
515
|
+
const policyDoc = read !== undefined ? read.layers.policySettings.doc : null;
|
|
516
|
+
const pluginOnlyRaw = policyDoc === null ? undefined : ownValue(policyDoc, 'strictPluginOnlyCustomization');
|
|
517
|
+
const pluginOnly = pluginOnlyRaw === true || (stringListOf(pluginOnlyRaw) ?? []).includes('mcp');
|
|
518
|
+
const enterpriseBranch = ent.status === 'loaded' || ent.status === 'unreadable';
|
|
519
|
+
const strict = ownValue(supplementary, 'strictDynamicOnly') === true;
|
|
520
|
+
const locked = enterpriseBranch ? 'enterprise_config' : pluginOnly ? 'plugin_only' : undefined;
|
|
521
|
+
const managedState = read?.managed;
|
|
522
|
+
const managedWithhold = managedState === 'in-effect-pieces' || managedState === 'remote-unknown' || managedState === 'unreadable-covered' || managedState === 'unreadable-uncovered';
|
|
523
|
+
const projectEligible = !pluginOnly && approvals.mayApprove;
|
|
524
|
+
const sources = [];
|
|
525
|
+
const lockedOut = [];
|
|
526
|
+
const push = (s, out = sources) => {
|
|
527
|
+
out.push(s);
|
|
528
|
+
};
|
|
529
|
+
const judged = (t, opts) => t.kind === 'absent' ? 'absent' : t.kind === 'bad' ? 'unreadable' : (judgeTable(t.table, env, opts) ?? 'unreadable');
|
|
530
|
+
const slot = (key) => {
|
|
531
|
+
const r = ownData(supplementary, key);
|
|
532
|
+
if (!r.ok)
|
|
533
|
+
return 'unreadable';
|
|
534
|
+
return r.value === undefined ? undefined : isRecord(r.value) ? r.value : 'unreadable';
|
|
535
|
+
};
|
|
536
|
+
const agentSlot = (() => {
|
|
537
|
+
const r = ownData(supplementary, 'agent');
|
|
538
|
+
if (!r.ok)
|
|
539
|
+
return 'unreadable';
|
|
540
|
+
const v = r.value;
|
|
541
|
+
if (v === undefined)
|
|
542
|
+
return undefined;
|
|
543
|
+
if (!isRecord(v))
|
|
544
|
+
return 'unreadable';
|
|
545
|
+
const servers = ownValue(v, 'servers');
|
|
546
|
+
return isRecord(servers) ? { fromFlagSettings: ownValue(v, 'fromFlagSettings') === true, servers } : 'unreadable';
|
|
547
|
+
})();
|
|
548
|
+
const addExtra = (scope, table, useReserved) => {
|
|
549
|
+
if (table === undefined)
|
|
550
|
+
return;
|
|
551
|
+
const entries = table === 'unreadable' ? 'unreadable' : judged({ kind: 'table', table }, { expand: true, reserved: useReserved ? reserved : none, lang, ...(scope === 'plugin' ? { signature: true } : {}) });
|
|
552
|
+
if (entries === 'unreadable')
|
|
553
|
+
unreadable.add('supplementary');
|
|
554
|
+
else if (entries !== 'absent')
|
|
555
|
+
push({ scope, file: 'supplementary', revision: null, entries, project: false, writable: false });
|
|
556
|
+
};
|
|
557
|
+
const addManaged = () => {
|
|
558
|
+
const entries = judged(policyDoc === null ? { kind: 'absent' } : subTableOf(policyDoc, ['managedMcpServers']), { expand: false, reserved: none, lang });
|
|
559
|
+
if (entries === 'unreadable')
|
|
560
|
+
unreadable.add('managed');
|
|
561
|
+
else if (entries !== 'absent')
|
|
562
|
+
push({ scope: 'managed', file: 'managed', revision: null, entries, project: false, writable: false });
|
|
563
|
+
};
|
|
564
|
+
if (managedWithhold)
|
|
565
|
+
unreadable.add('managed');
|
|
566
|
+
if (enterpriseBranch) {
|
|
567
|
+
addManaged();
|
|
568
|
+
const entries = judged(sourceTableOf(ent, PROJECT_FILE_PATH), { expand: true, reserved, lang });
|
|
569
|
+
if (entries === 'unreadable')
|
|
570
|
+
unreadable.add('enterprise');
|
|
571
|
+
else if (entries !== 'absent')
|
|
572
|
+
push({ scope: 'enterprise', file: 'enterprise', revision: revisionOf(ent), entries, project: false, writable: false });
|
|
573
|
+
}
|
|
574
|
+
else if (strict) {
|
|
575
|
+
if (agentSlot !== undefined && agentSlot !== 'unreadable' && agentSlot.fromFlagSettings)
|
|
576
|
+
addExtra('agent', agentSlot.servers, false);
|
|
577
|
+
else if (agentSlot === 'unreadable')
|
|
578
|
+
unreadable.add('supplementary');
|
|
579
|
+
addExtra('dynamic', slot('dynamic'), false);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
addExtra('claudeai', slot('claudeai'), true);
|
|
583
|
+
addExtra('plugin', slot('plugin'), true);
|
|
584
|
+
const fileSink = pluginOnly ? lockedOut : sources;
|
|
585
|
+
const userEntries = judged(sourceTableOf(g, USER_SCOPE_PATH), { expand: true, reserved, lang });
|
|
586
|
+
if (userEntries === 'unreadable') {
|
|
587
|
+
if (!pluginOnly)
|
|
588
|
+
unreadable.add('global');
|
|
589
|
+
}
|
|
590
|
+
else if (userEntries !== 'absent')
|
|
591
|
+
push({ scope: 'user', file: 'global', revision: revisionOf(g), entries: userEntries, project: false, writable: !pluginOnly }, fileSink);
|
|
592
|
+
for (let ci = 0; ci < chain.length; ci++) {
|
|
593
|
+
const f = chain[ci];
|
|
594
|
+
const entries = judged(sourceTableOf(f, PROJECT_FILE_PATH), { expand: true, reserved, lang });
|
|
595
|
+
if (entries === 'unreadable') {
|
|
596
|
+
if (projectEligible)
|
|
597
|
+
unreadable.add('project');
|
|
598
|
+
}
|
|
599
|
+
else if (entries !== 'absent')
|
|
600
|
+
push({ scope: 'project', file: 'project', revision: revisionOf(f), entries, project: true, writable: !pluginOnly && ci === chain.length - 1 && ownValue(facts, 'projectConfigRootSet') !== true }, fileSink);
|
|
601
|
+
}
|
|
602
|
+
if (approvals.rejectedUnreadable && projectEligible && sources.some((s) => s.project && s.entries.length > 0))
|
|
603
|
+
unreadable.add('project');
|
|
604
|
+
if (projectKey !== undefined) {
|
|
605
|
+
const localEntries = judged(sourceTableOf(g, localScopePath(projectKey)), { expand: true, reserved, lang });
|
|
606
|
+
if (localEntries === 'unreadable') {
|
|
607
|
+
if (!pluginOnly)
|
|
608
|
+
unreadable.add('global');
|
|
609
|
+
}
|
|
610
|
+
else if (localEntries !== 'absent')
|
|
611
|
+
push({ scope: 'local', file: 'global', revision: revisionOf(g), entries: localEntries, project: false, writable: !pluginOnly }, fileSink);
|
|
612
|
+
}
|
|
613
|
+
addManaged();
|
|
614
|
+
if (agentSlot === 'unreadable')
|
|
615
|
+
unreadable.add('supplementary');
|
|
616
|
+
else if (agentSlot !== undefined)
|
|
617
|
+
addExtra('agent', agentSlot.servers, false);
|
|
618
|
+
addExtra('dynamic', slot('dynamic'), false);
|
|
619
|
+
}
|
|
620
|
+
const approvalByName = new Map();
|
|
621
|
+
const participates = (s, e) => {
|
|
622
|
+
if (!s.project)
|
|
623
|
+
return true;
|
|
624
|
+
let v = approvalByName.get(e.name);
|
|
625
|
+
if (v === undefined) {
|
|
626
|
+
v = approvalOf(approvals, e.name);
|
|
627
|
+
approvalByName.set(e.name, v);
|
|
628
|
+
}
|
|
629
|
+
return v.state === 'approved';
|
|
630
|
+
};
|
|
631
|
+
const manualSigs = new Set();
|
|
632
|
+
for (const s of sources) {
|
|
633
|
+
if (s.scope === 'plugin' || s.scope === 'claudeai')
|
|
634
|
+
continue;
|
|
635
|
+
for (const e of s.entries)
|
|
636
|
+
if (e.spec !== undefined && participates(s, e)) {
|
|
637
|
+
const sig = signatureOf(e.raw);
|
|
638
|
+
if (sig !== undefined)
|
|
639
|
+
manualSigs.add(sig);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const pluginSigs = new Set();
|
|
643
|
+
const suppressed = new Set();
|
|
644
|
+
for (const s of sources) {
|
|
645
|
+
if (s.scope !== 'plugin')
|
|
646
|
+
continue;
|
|
647
|
+
for (const e of s.entries) {
|
|
648
|
+
if (e.envSig === undefined)
|
|
649
|
+
continue;
|
|
650
|
+
const sig = signatureOf(e.raw);
|
|
651
|
+
if (sig === undefined)
|
|
652
|
+
continue;
|
|
653
|
+
const withEnv = `${sig}|${e.envSig}`;
|
|
654
|
+
if (manualSigs.has(sig) || pluginSigs.has(withEnv))
|
|
655
|
+
suppressed.add(e);
|
|
656
|
+
else
|
|
657
|
+
pluginSigs.add(withEnv);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
const votes = new Map();
|
|
661
|
+
for (const s of sources)
|
|
662
|
+
for (const e of s.entries) {
|
|
663
|
+
if (e.vote.kind === 'none' || suppressed.has(e) || !participates(s, e))
|
|
664
|
+
continue;
|
|
665
|
+
const list = votes.get(e.name) ?? [];
|
|
666
|
+
list.push(e.vote);
|
|
667
|
+
votes.set(e.name, list);
|
|
668
|
+
}
|
|
669
|
+
const ledgerOf = (name) => {
|
|
670
|
+
const vs = votes.get(name) ?? [];
|
|
671
|
+
if (vs.some((v) => v.kind === 'bad'))
|
|
672
|
+
return { kind: 'withheld', by: 'narrowing_invalid' };
|
|
673
|
+
const lists = vs.filter((v) => v.kind === 'list');
|
|
674
|
+
if (lists.length === 0)
|
|
675
|
+
return { kind: 'ok' };
|
|
676
|
+
if (lists.length !== vs.length)
|
|
677
|
+
return { kind: 'withheld', by: 'narrowing_conflict' };
|
|
678
|
+
const first = JSON.stringify(lists[0].list);
|
|
679
|
+
return lists.every((l) => JSON.stringify(l.list) === first) ? { kind: 'ok' } : { kind: 'withheld', by: 'narrowing_conflict' };
|
|
680
|
+
};
|
|
681
|
+
let disabled = new Set();
|
|
682
|
+
if (projectKey !== undefined) {
|
|
683
|
+
const pt = sourceTableOf(g, ['projects', projectKey]);
|
|
684
|
+
const list = pt.kind === 'table' ? nameListOf(pt.table, 'disabledMcpServers') : pt.kind === 'absent' ? { kind: 'absent' } : { kind: 'bad' };
|
|
685
|
+
if (list.kind === 'bad')
|
|
686
|
+
unreadable.add('global');
|
|
687
|
+
else if (list.kind === 'list')
|
|
688
|
+
disabled = new Set(list.list);
|
|
689
|
+
}
|
|
690
|
+
const q2 = unreadable.size > 0;
|
|
691
|
+
const order = [];
|
|
692
|
+
const byName = new Map();
|
|
693
|
+
const slotOf = (name) => {
|
|
694
|
+
let x = byName.get(name);
|
|
695
|
+
if (x === undefined) {
|
|
696
|
+
x = { shadows: [] };
|
|
697
|
+
byName.set(name, x);
|
|
698
|
+
order.push(name);
|
|
699
|
+
}
|
|
700
|
+
return x;
|
|
701
|
+
};
|
|
702
|
+
for (const s of sources)
|
|
703
|
+
for (const e of s.entries) {
|
|
704
|
+
if (suppressed.has(e))
|
|
705
|
+
continue;
|
|
706
|
+
const x = slotOf(e.name);
|
|
707
|
+
if (s.project && !participates(s, e)) {
|
|
708
|
+
x.pending = { s, e, v: approvalByName.get(e.name) ?? { state: 'pending' } };
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
if (e.spec === undefined) {
|
|
712
|
+
x.unusable = { s, e };
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if (x.winner !== undefined && !x.shadows.includes(x.winner.s.scope))
|
|
716
|
+
x.shadows.push(x.winner.s.scope);
|
|
717
|
+
x.winner = { s, e };
|
|
718
|
+
}
|
|
719
|
+
for (const s of lockedOut)
|
|
720
|
+
for (const e of s.entries)
|
|
721
|
+
slotOf(e.name);
|
|
722
|
+
const rows = [];
|
|
723
|
+
for (const name of order) {
|
|
724
|
+
const x = byName.get(name);
|
|
725
|
+
if (x.winner !== undefined) {
|
|
726
|
+
const { s, e } = x.winner;
|
|
727
|
+
const ledger = ledgerOf(name);
|
|
728
|
+
const base = { name, scope: s.scope, entry: e.raw, shadows: Object.freeze([...x.shadows]), origin: Object.freeze({ file: s.file, revision: s.revision }), writable: s.writable, missing: e.missing, withheldVars: e.withheld };
|
|
729
|
+
const av = s.project ? approvalByName.get(name) : undefined;
|
|
730
|
+
const approvedBy = av !== undefined && av.state === 'approved' ? av.by : undefined;
|
|
731
|
+
const withApproval = approvedBy === undefined ? {} : { approvedBy };
|
|
732
|
+
if (q2)
|
|
733
|
+
rows.push(Object.freeze({ ...base, ...withApproval, state: 'withheld', withheldBy: 'layer_unreadable' }));
|
|
734
|
+
else if (ledger.kind === 'withheld')
|
|
735
|
+
rows.push(Object.freeze({ ...base, ...withApproval, state: 'withheld', withheldBy: ledger.by }));
|
|
736
|
+
else if (disabled.has(name))
|
|
737
|
+
rows.push(Object.freeze({ ...base, ...withApproval, state: 'disabled' }));
|
|
738
|
+
else
|
|
739
|
+
rows.push(Object.freeze({ ...base, ...withApproval, state: 'active', ...(e.spec === undefined ? {} : { spec: e.spec }) }));
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (x.pending !== undefined) {
|
|
743
|
+
const { s, e, v } = x.pending;
|
|
744
|
+
rows.push(Object.freeze({ name, scope: s.scope, entry: e.raw, shadows: Object.freeze([]), state: v.state === 'rejected' ? 'rejected' : 'pending_approval', origin: Object.freeze({ file: s.file, revision: s.revision }), writable: s.writable, missing: e.missing, withheldVars: e.withheld }));
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
if (x.unusable !== undefined) {
|
|
748
|
+
const { s, e } = x.unusable;
|
|
749
|
+
rows.push(Object.freeze({ name, scope: s.scope, entry: e.raw, shadows: Object.freeze([]), state: 'unusable', problems: e.problems, origin: Object.freeze({ file: s.file, revision: s.revision }), writable: s.writable, missing: e.missing, withheldVars: e.withheld }));
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
for (const s of lockedOut) {
|
|
753
|
+
const e = s.entries.filter((y) => y.name === name).pop();
|
|
754
|
+
if (e === undefined)
|
|
755
|
+
continue;
|
|
756
|
+
rows.push(Object.freeze({ name, scope: s.scope, entry: e.raw, shadows: Object.freeze([]), state: 'withheld', withheldBy: 'policy_locked', origin: Object.freeze({ file: s.file, revision: s.revision }), writable: false, missing: e.missing, withheldVars: e.withheld }));
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
const dep = ownValue(facts, 'deploymentNames');
|
|
761
|
+
const depList = stringListOf(dep);
|
|
762
|
+
const deployment = depList !== undefined ? Object.freeze({ state: 'read', names: Object.freeze(depList) }) : Object.freeze({ state: 'unknown' });
|
|
763
|
+
return Object.freeze({
|
|
764
|
+
rows: Object.freeze(rows),
|
|
765
|
+
unreadable: Object.freeze(['global', 'project', 'enterprise', 'managed', 'supplementary'].filter((f) => unreadable.has(f))),
|
|
766
|
+
locked,
|
|
767
|
+
purpose,
|
|
768
|
+
approvalsWithheld: approvals.withheld,
|
|
769
|
+
deployment,
|
|
770
|
+
pluginRoster: slot('plugin') === undefined ? 'absent' : 'host_supplied',
|
|
771
|
+
promptsAndResources: 'not_yet',
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
export function mcpServersReport(files, facts, settings, supplementarySources, lang) {
|
|
775
|
+
return mergeMcpServers({ files, facts, settings, ...(supplementarySources === undefined ? {} : { supplementarySources }), ...(lang === undefined ? {} : { lang }) }, 'report');
|
|
776
|
+
}
|