@respira/wordpress-mcp-server 8.3.6 → 8.3.8
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 +12 -0
- package/dist/__tests__/429299b5-delete-post-empty-envelope.test.d.ts +46 -0
- package/dist/__tests__/429299b5-delete-post-empty-envelope.test.d.ts.map +1 -0
- package/dist/__tests__/429299b5-delete-post-empty-envelope.test.js +360 -0
- package/dist/__tests__/429299b5-delete-post-empty-envelope.test.js.map +1 -0
- package/dist/__tests__/agent-signature-clientinfo.test.js +11 -0
- package/dist/__tests__/agent-signature-clientinfo.test.js.map +1 -1
- package/dist/__tests__/stale-build-and-refresh-silence.test.d.ts +2 -0
- package/dist/__tests__/stale-build-and-refresh-silence.test.d.ts.map +1 -0
- package/dist/__tests__/stale-build-and-refresh-silence.test.js +76 -0
- package/dist/__tests__/stale-build-and-refresh-silence.test.js.map +1 -0
- package/dist/agent-signature.d.ts.map +1 -1
- package/dist/agent-signature.js +13 -0
- package/dist/agent-signature.js.map +1 -1
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +116 -20
- package/dist/server.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +52 -3
- package/dist/setup.js.map +1 -1
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/version-checker.d.ts +8 -0
- package/dist/version-checker.d.ts.map +1 -1
- package/dist/version-checker.js +39 -0
- package/dist/version-checker.js.map +1 -1
- package/dist/wordpress-client.d.ts +52 -6
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +107 -7
- package/dist/wordpress-client.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to Respira WordPress MCP Server will be documented in this f
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [8.3.7] - 2026-08-19
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`delete_post` answered with an empty response and deleted nothing.** The tool is approval-gated on the site, and the gate replies with a token and HTTP 202. 202 is a success, so the request resolved normally rather than raising, and the client method then discarded the body and returned nothing. What reached the caller was a response carrying only the site block: no error to react to, no token to use, and nothing in the activity log either. The body is now returned, so the token arrives and sending it back finishes the delete. `delete_media` had the same handshake broken one layer lower: the dispatcher never forwarded the token it had been given, so the site saw a first-time call every time and issued a new token, forever. Both dispatch sites now forward every parameter their schemas advertise.
|
|
13
|
+
- **`update_element` and `remove_element` rejected the two things the ambiguity error told you to use.** When several elements matched, the site refused to guess and suggested addressing the element by `path` or narrowing with `match_content`. Neither was in either tool's schema, so a strict client could not send them at all, and `find_element` did not declare `path` either. All three now accept them, and `find_element` also declares `text` and `admin_label`. Requires plugin 8.6.33, which makes both remedies actually resolve the ambiguity rather than merely being accepted.
|
|
14
|
+
- **`bulk_pages_operation` documented a rate limit six times stricter than the real one.** It said three runs an hour; the limit is twenty, and dry runs have never counted. Agents plan their work around that number. Corrected, along with the note that a list of search terms is now a supported shape rather than a 500.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **`create_theme_builder_template` no longer assigns anything by default.** Omitting `assignments` used to mean site-wide, which on a body template replaced the content of every page. It now creates the template unassigned, and site-wide has to be requested. The schema documents `confirm_live_edit` for the case where an explicit site-wide assignment would claim a slot on a default template that already exists. Requires plugin 8.6.33.
|
|
19
|
+
|
|
8
20
|
## [8.3.6] - 2026-08-19
|
|
9
21
|
|
|
10
22
|
### Added
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for ticket 429299b5: "respira_delete_post silently does
|
|
3
|
+
* nothing and returns an empty response" (smartgard.ai, plugin 8.6.31 /
|
|
4
|
+
* mcp 8.3.5).
|
|
5
|
+
*
|
|
6
|
+
* Reported symptom: `delete_post` on a genuine Respira-created duplicate came
|
|
7
|
+
* back with a body whose ONLY content was the site block —
|
|
8
|
+
*
|
|
9
|
+
* {"site":{"id":"env-site","name":"WordPress (from env)", ... }}
|
|
10
|
+
*
|
|
11
|
+
* — no success flag, no error, no approval token. The post was untouched, and
|
|
12
|
+
* `list_activity` had no entry for either attempt, which read like the call
|
|
13
|
+
* had been stopped inside the MCP server and never dispatched.
|
|
14
|
+
*
|
|
15
|
+
* Two independent defects, both in this package:
|
|
16
|
+
*
|
|
17
|
+
* 1. `WordPressClient.deletePost()` was `Promise<void>` and threw
|
|
18
|
+
* `response.data` away. The WP approval gate answers a first-time
|
|
19
|
+
* destructive call with `respira_approval_required` at HTTP **202** — a
|
|
20
|
+
* 2xx, so axios resolves rather than throws — carrying
|
|
21
|
+
* `data.approval_request.approval_token`. That entire body was discarded,
|
|
22
|
+
* `undefined` reached `withSiteContext`, which wraps a non-object as
|
|
23
|
+
* `{ result, site }`, and `JSON.stringify` drops the `undefined` key. What
|
|
24
|
+
* the agent received was literally `{"site":{...}}`. This is the same bug
|
|
25
|
+
* `deletePage` had and fixed in v6.11.12; posts were never brought along.
|
|
26
|
+
*
|
|
27
|
+
* 2. `wordpress_delete_media` dispatched as `client.deleteMedia(args.id)`,
|
|
28
|
+
* dropping the `approval_token` the schema advertises and the agent sends.
|
|
29
|
+
* The plugin therefore saw a fresh first call every time and dutifully
|
|
30
|
+
* minted a new token — a token that only ever yields another token.
|
|
31
|
+
* Ticket 927aa7ed fixed the client METHOD and tested the client method, so
|
|
32
|
+
* it passed green against a tool that was still broken end to end. That is
|
|
33
|
+
* why the assertions below run through the dispatcher.
|
|
34
|
+
*
|
|
35
|
+
* The fake site here implements the plugin's real approval semantics
|
|
36
|
+
* (Respira_Tool_Governance): mint on a tokenless call, execute on a call that
|
|
37
|
+
* echoes a known token, and count how many tokens were ever minted so a
|
|
38
|
+
* mint-instead-of-validate loop fails loudly.
|
|
39
|
+
*
|
|
40
|
+
* Mock-adapter plumbing mirrors acf-site-id-routing.test.ts and
|
|
41
|
+
* delete-media-approval-token.test.ts.
|
|
42
|
+
*
|
|
43
|
+
* @since 8.3.7 (ticket 429299b5)
|
|
44
|
+
*/
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=429299b5-delete-post-empty-envelope.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"429299b5-delete-post-empty-envelope.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/429299b5-delete-post-empty-envelope.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG"}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for ticket 429299b5: "respira_delete_post silently does
|
|
3
|
+
* nothing and returns an empty response" (smartgard.ai, plugin 8.6.31 /
|
|
4
|
+
* mcp 8.3.5).
|
|
5
|
+
*
|
|
6
|
+
* Reported symptom: `delete_post` on a genuine Respira-created duplicate came
|
|
7
|
+
* back with a body whose ONLY content was the site block —
|
|
8
|
+
*
|
|
9
|
+
* {"site":{"id":"env-site","name":"WordPress (from env)", ... }}
|
|
10
|
+
*
|
|
11
|
+
* — no success flag, no error, no approval token. The post was untouched, and
|
|
12
|
+
* `list_activity` had no entry for either attempt, which read like the call
|
|
13
|
+
* had been stopped inside the MCP server and never dispatched.
|
|
14
|
+
*
|
|
15
|
+
* Two independent defects, both in this package:
|
|
16
|
+
*
|
|
17
|
+
* 1. `WordPressClient.deletePost()` was `Promise<void>` and threw
|
|
18
|
+
* `response.data` away. The WP approval gate answers a first-time
|
|
19
|
+
* destructive call with `respira_approval_required` at HTTP **202** — a
|
|
20
|
+
* 2xx, so axios resolves rather than throws — carrying
|
|
21
|
+
* `data.approval_request.approval_token`. That entire body was discarded,
|
|
22
|
+
* `undefined` reached `withSiteContext`, which wraps a non-object as
|
|
23
|
+
* `{ result, site }`, and `JSON.stringify` drops the `undefined` key. What
|
|
24
|
+
* the agent received was literally `{"site":{...}}`. This is the same bug
|
|
25
|
+
* `deletePage` had and fixed in v6.11.12; posts were never brought along.
|
|
26
|
+
*
|
|
27
|
+
* 2. `wordpress_delete_media` dispatched as `client.deleteMedia(args.id)`,
|
|
28
|
+
* dropping the `approval_token` the schema advertises and the agent sends.
|
|
29
|
+
* The plugin therefore saw a fresh first call every time and dutifully
|
|
30
|
+
* minted a new token — a token that only ever yields another token.
|
|
31
|
+
* Ticket 927aa7ed fixed the client METHOD and tested the client method, so
|
|
32
|
+
* it passed green against a tool that was still broken end to end. That is
|
|
33
|
+
* why the assertions below run through the dispatcher.
|
|
34
|
+
*
|
|
35
|
+
* The fake site here implements the plugin's real approval semantics
|
|
36
|
+
* (Respira_Tool_Governance): mint on a tokenless call, execute on a call that
|
|
37
|
+
* echoes a known token, and count how many tokens were ever minted so a
|
|
38
|
+
* mint-instead-of-validate loop fails loudly.
|
|
39
|
+
*
|
|
40
|
+
* Mock-adapter plumbing mirrors acf-site-id-routing.test.ts and
|
|
41
|
+
* delete-media-approval-token.test.ts.
|
|
42
|
+
*
|
|
43
|
+
* @since 8.3.7 (ticket 429299b5)
|
|
44
|
+
*/
|
|
45
|
+
import { test } from 'node:test';
|
|
46
|
+
import assert from 'node:assert/strict';
|
|
47
|
+
import axios from 'axios';
|
|
48
|
+
import { readFileSync } from 'node:fs';
|
|
49
|
+
import { RespiraWordPressServer } from '../server.js';
|
|
50
|
+
const callLog = [];
|
|
51
|
+
let currentHandler = null;
|
|
52
|
+
const originalAdapter = axios.defaults.adapter;
|
|
53
|
+
function installMockAdapter() {
|
|
54
|
+
callLog.length = 0;
|
|
55
|
+
axios.defaults.adapter = ((config) => {
|
|
56
|
+
callLog.push({
|
|
57
|
+
method: (config.method || 'get').toLowerCase(),
|
|
58
|
+
url: config.url,
|
|
59
|
+
params: config.params,
|
|
60
|
+
});
|
|
61
|
+
if (!currentHandler) {
|
|
62
|
+
return Promise.reject(new Error('no mock handler installed'));
|
|
63
|
+
}
|
|
64
|
+
const r = currentHandler(config);
|
|
65
|
+
const response = {
|
|
66
|
+
status: r.status,
|
|
67
|
+
statusText: 'OK',
|
|
68
|
+
headers: { 'content-type': 'application/json' },
|
|
69
|
+
config,
|
|
70
|
+
data: r.data,
|
|
71
|
+
request: {},
|
|
72
|
+
};
|
|
73
|
+
// Mirror axios' own validateStatus default: 2xx resolves, everything else
|
|
74
|
+
// rejects. The 202 approval gate resolving rather than throwing is the
|
|
75
|
+
// whole reason defect 1 was invisible, so the mock must not smooth it over.
|
|
76
|
+
if (r.status >= 200 && r.status < 300) {
|
|
77
|
+
return Promise.resolve(response);
|
|
78
|
+
}
|
|
79
|
+
const err = new Error(`Request failed with status code ${r.status}`);
|
|
80
|
+
err.isAxiosError = true;
|
|
81
|
+
err.response = response;
|
|
82
|
+
err.config = config;
|
|
83
|
+
return Promise.reject(err);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function restoreAdapter() {
|
|
87
|
+
axios.defaults.adapter = originalAdapter;
|
|
88
|
+
currentHandler = null;
|
|
89
|
+
}
|
|
90
|
+
const SITE = {
|
|
91
|
+
id: 'env-site',
|
|
92
|
+
name: 'WordPress (from env)',
|
|
93
|
+
url: 'https://smartgard.ai',
|
|
94
|
+
apiKey: 'respira_test_key',
|
|
95
|
+
default: true,
|
|
96
|
+
};
|
|
97
|
+
function approvalGatedSite(opts) {
|
|
98
|
+
const site = { minted: [], consumed: [], deleted: [], handler: null };
|
|
99
|
+
site.handler = (config) => {
|
|
100
|
+
const url = config.url || '';
|
|
101
|
+
const method = (config.method || 'get').toLowerCase();
|
|
102
|
+
const params = config.params || {};
|
|
103
|
+
if (method === 'delete' && opts.pathRe.test(url)) {
|
|
104
|
+
const id = Number(url.split('/').pop());
|
|
105
|
+
const token = params.approval_token;
|
|
106
|
+
if (token) {
|
|
107
|
+
if (!site.minted.includes(token)) {
|
|
108
|
+
// The plugin returns 403 respira_invalid_approval_token here.
|
|
109
|
+
return {
|
|
110
|
+
status: 403,
|
|
111
|
+
data: {
|
|
112
|
+
code: 'respira_invalid_approval_token',
|
|
113
|
+
message: 'Approval token is invalid or has expired. Request approval again.',
|
|
114
|
+
data: { status: 403 },
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
site.consumed.push(token);
|
|
119
|
+
site.deleted.push(id);
|
|
120
|
+
return { status: 200, data: { success: true, message: 'Post deleted successfully.' } };
|
|
121
|
+
}
|
|
122
|
+
// No token: mint one and refuse. HTTP 202 — a 2xx, deliberately.
|
|
123
|
+
const fresh = `tok-${site.minted.length + 1}`;
|
|
124
|
+
site.minted.push(fresh);
|
|
125
|
+
return {
|
|
126
|
+
status: 202,
|
|
127
|
+
data: {
|
|
128
|
+
code: 'respira_approval_required',
|
|
129
|
+
message: `The "${opts.toolName}" tool requires explicit approval before execution.`,
|
|
130
|
+
data: {
|
|
131
|
+
status: 202,
|
|
132
|
+
approval_request: {
|
|
133
|
+
tool_name: opts.toolName,
|
|
134
|
+
predicted_effect: `Permanently delete ${opts.toolName.replace('delete_', '')} ID ${id}.`,
|
|
135
|
+
approval_token: fresh,
|
|
136
|
+
expires_in: 600,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
// Any probe (version negotiation, compatibility) answers harmlessly.
|
|
143
|
+
return { status: 200, data: {} };
|
|
144
|
+
};
|
|
145
|
+
return site;
|
|
146
|
+
}
|
|
147
|
+
/** Run a tool the way production does: dispatch, then wrap in the site envelope. */
|
|
148
|
+
async function callTool(server, name, args) {
|
|
149
|
+
const result = await server.dispatchToolCall(name, args);
|
|
150
|
+
return server.withSiteContext(result, args);
|
|
151
|
+
}
|
|
152
|
+
/** The exact failure shape the customer saw. */
|
|
153
|
+
function isBareSiteEnvelope(value) {
|
|
154
|
+
if (!value || typeof value !== 'object') {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const serialized = JSON.parse(JSON.stringify(value));
|
|
158
|
+
const keys = Object.keys(serialized);
|
|
159
|
+
return keys.length === 1 && keys[0] === 'site';
|
|
160
|
+
}
|
|
161
|
+
// -----------------------------------------------------------------------------
|
|
162
|
+
// delete_post
|
|
163
|
+
// -----------------------------------------------------------------------------
|
|
164
|
+
test('delete_post never returns a bare site envelope (429299b5)', async () => {
|
|
165
|
+
installMockAdapter();
|
|
166
|
+
try {
|
|
167
|
+
const site = approvalGatedSite({ pathRe: /^\/posts\/\d+$/, toolName: 'delete_post' });
|
|
168
|
+
currentHandler = site.handler;
|
|
169
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
170
|
+
const result = await callTool(server, 'wordpress_delete_post', { id: 102058 });
|
|
171
|
+
assert.equal(isBareSiteEnvelope(result), false, 'delete_post returned {"site":{...}} and nothing else — the exact 429299b5 symptom');
|
|
172
|
+
assert.ok(result.site, 'the site block should still be present');
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
restoreAdapter();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
test('delete_post surfaces the approval request as an actionable payload (429299b5)', async () => {
|
|
179
|
+
installMockAdapter();
|
|
180
|
+
try {
|
|
181
|
+
const site = approvalGatedSite({ pathRe: /^\/posts\/\d+$/, toolName: 'delete_post' });
|
|
182
|
+
currentHandler = site.handler;
|
|
183
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
184
|
+
const result = await callTool(server, 'wordpress_delete_post', { id: 102058 });
|
|
185
|
+
assert.equal(result.code, 'respira_approval_required');
|
|
186
|
+
assert.ok(result.message, 'a refusal must carry a message an agent can relay');
|
|
187
|
+
const token = result?.data?.approval_request?.approval_token;
|
|
188
|
+
assert.equal(token, 'tok-1', 'the approval token must reach the caller; discarding it is what made the tool unusable');
|
|
189
|
+
assert.equal(result.data.approval_request.tool_name, 'delete_post', 'the payload must name the tool the token is bound to');
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
restoreAdapter();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
test('the delete_post approval token completes the delete instead of minting another (429299b5)', async () => {
|
|
196
|
+
installMockAdapter();
|
|
197
|
+
try {
|
|
198
|
+
const site = approvalGatedSite({ pathRe: /^\/posts\/\d+$/, toolName: 'delete_post' });
|
|
199
|
+
currentHandler = site.handler;
|
|
200
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
201
|
+
const first = await callTool(server, 'wordpress_delete_post', { id: 102058 });
|
|
202
|
+
const token = first.data.approval_request.approval_token;
|
|
203
|
+
const second = await callTool(server, 'wordpress_delete_post', {
|
|
204
|
+
id: 102058,
|
|
205
|
+
approval_token: token,
|
|
206
|
+
});
|
|
207
|
+
assert.equal(second.success, true, 'the second call must complete the delete');
|
|
208
|
+
assert.equal(isBareSiteEnvelope(second), false);
|
|
209
|
+
assert.deepEqual(site.consumed, [token], 'the site must have executed against the echoed token');
|
|
210
|
+
assert.deepEqual(site.deleted, [102058], 'the post must actually be gone');
|
|
211
|
+
assert.equal(site.minted.length, 1, 'a second token was minted — the token was not forwarded, so this is the unbreakable loop again');
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
restoreAdapter();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
test('delete_post forwards the token verbatim on the wire (429299b5)', async () => {
|
|
218
|
+
installMockAdapter();
|
|
219
|
+
try {
|
|
220
|
+
const site = approvalGatedSite({ pathRe: /^\/posts\/\d+$/, toolName: 'delete_post' });
|
|
221
|
+
site.minted.push('tok-1'); // pretend an earlier call already handed this out
|
|
222
|
+
currentHandler = site.handler;
|
|
223
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
224
|
+
await callTool(server, 'wordpress_delete_post', { id: 7, approval_token: 'tok-1' });
|
|
225
|
+
const deletes = callLog.filter((c) => c.method === 'delete' && /^\/posts\//.test(c.url));
|
|
226
|
+
assert.equal(deletes.length, 1);
|
|
227
|
+
assert.equal(deletes[0].params?.approval_token, 'tok-1');
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
restoreAdapter();
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
test('delete_post refuses structurally, never silently (429299b5)', async () => {
|
|
234
|
+
installMockAdapter();
|
|
235
|
+
try {
|
|
236
|
+
// A refusal the plugin raises at 403: not a duplicate, direct editing off.
|
|
237
|
+
currentHandler = (config) => {
|
|
238
|
+
if ((config.method || '').toLowerCase() === 'delete') {
|
|
239
|
+
return {
|
|
240
|
+
status: 403,
|
|
241
|
+
data: {
|
|
242
|
+
code: 'respira_not_duplicate',
|
|
243
|
+
message: 'Cannot delete original post (ID: 100952). Only Respira duplicates can be deleted.',
|
|
244
|
+
data: { status: 403, original_id: 100952, instructions: ['Duplicate it first.'] },
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return { status: 200, data: {} };
|
|
249
|
+
};
|
|
250
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
251
|
+
await assert.rejects(() => callTool(server, 'wordpress_delete_post', { id: 100952 }), (err) => {
|
|
252
|
+
// A thrown coded error is a structured refusal: the agent gets the
|
|
253
|
+
// plugin's reason. What must never happen is a resolved empty envelope.
|
|
254
|
+
assert.match(String(err.message), /Only Respira duplicates can be deleted/);
|
|
255
|
+
return true;
|
|
256
|
+
}, 'a 403 refusal must surface as an error, not resolve into an empty envelope');
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
restoreAdapter();
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
test('delete_post does not smuggle confirm_live_edit in behind force (429299b5)', async () => {
|
|
263
|
+
installMockAdapter();
|
|
264
|
+
try {
|
|
265
|
+
const site = approvalGatedSite({ pathRe: /^\/posts\/\d+$/, toolName: 'delete_post' });
|
|
266
|
+
currentHandler = site.handler;
|
|
267
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
268
|
+
await callTool(server, 'wordpress_delete_post', { id: 5, force: true });
|
|
269
|
+
const forced = callLog.filter((c) => c.method === 'delete').pop();
|
|
270
|
+
assert.equal(forced?.params?.force, true);
|
|
271
|
+
assert.equal(forced?.params?.confirm_live_edit, undefined, 'force alone must not be escalated into "yes, delete the live original"');
|
|
272
|
+
await callTool(server, 'wordpress_delete_post', { id: 5, force: true, confirm_live_edit: true });
|
|
273
|
+
const confirmed = callLog.filter((c) => c.method === 'delete').pop();
|
|
274
|
+
assert.equal(confirmed?.params?.confirm_live_edit, true, 'explicit confirmation must reach the site');
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
restoreAdapter();
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
// -----------------------------------------------------------------------------
|
|
281
|
+
// delete_media (the second defect in the same ticket)
|
|
282
|
+
// -----------------------------------------------------------------------------
|
|
283
|
+
test('delete_media completes on the returned token rather than looping (429299b5)', async () => {
|
|
284
|
+
installMockAdapter();
|
|
285
|
+
try {
|
|
286
|
+
const site = approvalGatedSite({ pathRe: /^\/media\/\d+$/, toolName: 'delete_media' });
|
|
287
|
+
currentHandler = site.handler;
|
|
288
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
289
|
+
const first = await callTool(server, 'wordpress_delete_media', { id: 4242 });
|
|
290
|
+
assert.equal(isBareSiteEnvelope(first), false);
|
|
291
|
+
const token = first?.data?.approval_request?.approval_token;
|
|
292
|
+
assert.equal(token, 'tok-1', 'the first call must hand back a token');
|
|
293
|
+
const second = await callTool(server, 'wordpress_delete_media', {
|
|
294
|
+
id: 4242,
|
|
295
|
+
approval_token: token,
|
|
296
|
+
});
|
|
297
|
+
assert.equal(second.success, true);
|
|
298
|
+
assert.equal(site.minted.length, 1, 'the dispatcher dropped approval_token again: the site minted a second token instead of executing');
|
|
299
|
+
assert.deepEqual(site.deleted, [4242]);
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
restoreAdapter();
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
test('delete_media forwards force from the dispatcher (429299b5)', async () => {
|
|
306
|
+
installMockAdapter();
|
|
307
|
+
try {
|
|
308
|
+
const site = approvalGatedSite({ pathRe: /^\/media\/\d+$/, toolName: 'delete_media' });
|
|
309
|
+
site.minted.push('tok-1'); // pretend an earlier call already handed this out
|
|
310
|
+
currentHandler = site.handler;
|
|
311
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
312
|
+
await callTool(server, 'wordpress_delete_media', {
|
|
313
|
+
id: 9,
|
|
314
|
+
force: true,
|
|
315
|
+
approval_token: 'tok-1',
|
|
316
|
+
});
|
|
317
|
+
const del = callLog.filter((c) => c.method === 'delete' && /^\/media\//.test(c.url)).pop();
|
|
318
|
+
assert.equal(del?.params?.force, true, 'force is in the schema and must reach the site');
|
|
319
|
+
assert.equal(del?.params?.approval_token, 'tok-1');
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
restoreAdapter();
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
// -----------------------------------------------------------------------------
|
|
326
|
+
// The envelope rule itself
|
|
327
|
+
// -----------------------------------------------------------------------------
|
|
328
|
+
test('a bodyless 2xx delete still produces a structured result, not an empty envelope (429299b5)', async () => {
|
|
329
|
+
installMockAdapter();
|
|
330
|
+
try {
|
|
331
|
+
currentHandler = (config) => {
|
|
332
|
+
if ((config.method || '').toLowerCase() === 'delete') {
|
|
333
|
+
return { status: 204, data: '' };
|
|
334
|
+
}
|
|
335
|
+
return { status: 200, data: {} };
|
|
336
|
+
};
|
|
337
|
+
const server = new RespiraWordPressServer([SITE]);
|
|
338
|
+
const result = await callTool(server, 'wordpress_delete_post', { id: 1 });
|
|
339
|
+
assert.equal(isBareSiteEnvelope(result), false);
|
|
340
|
+
assert.equal(result.success, true);
|
|
341
|
+
assert.equal(result.body_was_empty, true);
|
|
342
|
+
assert.match(String(result.message), /no response body/);
|
|
343
|
+
}
|
|
344
|
+
finally {
|
|
345
|
+
restoreAdapter();
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
// getTools() runs capability negotiation against the live site, so the schema
|
|
349
|
+
// is asserted against the SOURCE instead — same approach as
|
|
350
|
+
// woo-card-tools-forward-fse-params.test.ts.
|
|
351
|
+
test('the delete_post schema documents the approval round-trip (429299b5)', () => {
|
|
352
|
+
const src = readFileSync(`${process.cwd()}/src/server.ts`, 'utf8');
|
|
353
|
+
const start = src.indexOf("name: 'wordpress_delete_post',");
|
|
354
|
+
assert.notEqual(start, -1, 'wordpress_delete_post must be registered');
|
|
355
|
+
const block = src.slice(start, src.indexOf('destructiveHint', start));
|
|
356
|
+
assert.match(block, /approval_token:/, 'the agent cannot complete the handshake without a declared approval_token param');
|
|
357
|
+
assert.match(block, /confirm_live_edit:/, 'the plugin requires confirm_live_edit alongside force; the schema must say so');
|
|
358
|
+
assert.match(block, /respira_approval_required/, 'the description must name the code an agent has to branch on');
|
|
359
|
+
});
|
|
360
|
+
//# sourceMappingURL=429299b5-delete-post-empty-envelope.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"429299b5-delete-post-empty-envelope.test.js","sourceRoot":"","sources":["../../src/__tests__/429299b5-delete-post-empty-envelope.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,17 @@ test('claude-code self-identification stays claude-code', () => {
|
|
|
21
21
|
setMcpClientInfo('claude-code', '2.0.0');
|
|
22
22
|
assert.strictEqual(detectRespiraAgentSignature().client, 'claude-code');
|
|
23
23
|
});
|
|
24
|
+
test('hermes agent normalizes to hermes however it names itself', () => {
|
|
25
|
+
setMcpClientInfo('hermes', '0.1');
|
|
26
|
+
assert.strictEqual(detectRespiraAgentSignature().client, 'hermes');
|
|
27
|
+
setMcpClientInfo('Hermes Agent 0.5 (desktop)', '0.5');
|
|
28
|
+
const sig = detectRespiraAgentSignature();
|
|
29
|
+
assert.strictEqual(sig.client, 'hermes');
|
|
30
|
+
assert.strictEqual(sig.source, 'client-info');
|
|
31
|
+
assert.strictEqual(sig.version, '0.5');
|
|
32
|
+
setMcpClientInfo('hermes-probe', '0.1');
|
|
33
|
+
assert.strictEqual(detectRespiraAgentSignature().client, 'hermes');
|
|
34
|
+
});
|
|
24
35
|
test('unknown but self-declared clients keep their sanitized name', () => {
|
|
25
36
|
setMcpClientInfo('SomeNew Agent!', null);
|
|
26
37
|
const sig = detectRespiraAgentSignature();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-signature-clientinfo.test.js","sourceRoot":"","sources":["../../src/__tests__/agent-signature-clientinfo.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEtF,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IACxC,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACpC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAEnC,gBAAgB,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,2BAA2B,EAAE,CAAC;IAC1C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACjD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mDAAmD,EAAE,GAAG,EAAE;IAC7D,gBAAgB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACzC,MAAM,CAAC,WAAW,CAAC,2BAA2B,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC1E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,GAAG,EAAE;IACvE,gBAAgB,CAAC,gBAAgB,EAAE,IAAW,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,2BAA2B,EAAE,CAAC;IAC1C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,eAAe,CAAC;IACnD,MAAM,GAAG,GAAG,2BAA2B,EAAE,CAAC;IAC1C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAChD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;AAC1C,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"agent-signature-clientinfo.test.js","sourceRoot":"","sources":["../../src/__tests__/agent-signature-clientinfo.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEtF,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IACxC,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACpC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAEnC,gBAAgB,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,2BAA2B,EAAE,CAAC;IAC1C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACjD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mDAAmD,EAAE,GAAG,EAAE;IAC7D,gBAAgB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACzC,MAAM,CAAC,WAAW,CAAC,2BAA2B,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC1E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACrE,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClC,MAAM,CAAC,WAAW,CAAC,2BAA2B,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEnE,gBAAgB,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,2BAA2B,EAAE,CAAC;IAC1C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAEvC,gBAAgB,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,WAAW,CAAC,2BAA2B,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,GAAG,EAAE;IACvE,gBAAgB,CAAC,gBAAgB,EAAE,IAAW,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,2BAA2B,EAAE,CAAC;IAC1C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,eAAe,CAAC;IACnD,MAAM,GAAG,GAAG,2BAA2B,EAAE,CAAC;IAC1C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAChD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;AAC1C,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stale-build-and-refresh-silence.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/stale-build-and-refresh-silence.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { RespiraVersionChecker } from '../version-checker.js';
|
|
5
|
+
/**
|
|
6
|
+
* Two silences that cost a customer an afternoon, reported 2026-08-20.
|
|
7
|
+
*
|
|
8
|
+
* She added sites on the dashboard and Claude never saw them. Only deleting
|
|
9
|
+
* and reinstalling the connector worked, so she and her AI concluded the
|
|
10
|
+
* connector had a bug. They were right, and it had been fixed eleven days
|
|
11
|
+
* earlier in 8.2.4. She was running 7.1.2.
|
|
12
|
+
*
|
|
13
|
+
* The update notice fired on every single tool call the whole time. It said
|
|
14
|
+
* "a newer MCP version is available", which is true, generic, and ignorable.
|
|
15
|
+
* It never said that the thing she was fighting was the thing the update
|
|
16
|
+
* fixed, so there was nothing to connect the two.
|
|
17
|
+
*
|
|
18
|
+
* The second silence is the same shape one layer down: when the site list
|
|
19
|
+
* cannot be refreshed at all, the refresh returned 0 exactly as it does when
|
|
20
|
+
* there is genuinely nothing new. "Your new site is missing" and "I never
|
|
21
|
+
* looked" were indistinguishable.
|
|
22
|
+
*/
|
|
23
|
+
const PKG = '@respira/wordpress-mcp-server';
|
|
24
|
+
test('a build predating the site-refresh fix is told what it is missing', () => {
|
|
25
|
+
const checker = new RespiraVersionChecker(PKG, '7.1.2');
|
|
26
|
+
const missing = checker.missingFixesFor('7.1.2');
|
|
27
|
+
assert.ok(missing.length > 0, 'a 7.1.2 build must be told it is missing behaviour');
|
|
28
|
+
const joined = missing.join(' ');
|
|
29
|
+
// Phrased as the symptom she would recognise, not as a changelog entry.
|
|
30
|
+
assert.match(joined, /added on the dashboard do not appear/i);
|
|
31
|
+
assert.match(joined, /deleted and reinstalled/i);
|
|
32
|
+
});
|
|
33
|
+
test('the symptom is named, not just the version number', () => {
|
|
34
|
+
const checker = new RespiraVersionChecker(PKG, '7.1.2');
|
|
35
|
+
const joined = checker.missingFixesFor('7.1.2').join(' ');
|
|
36
|
+
// The whole failure was a notice that described itself instead of the user's
|
|
37
|
+
// problem. Guard against sliding back to that.
|
|
38
|
+
assert.ok(!/^\s*a newer (mcp )?version is available/i.test(joined), 'the gap text must describe the user-visible symptom, not restate that an update exists');
|
|
39
|
+
});
|
|
40
|
+
test('a current build is told nothing, so the notice stays meaningful', () => {
|
|
41
|
+
const checker = new RespiraVersionChecker(PKG, '99.0.0');
|
|
42
|
+
assert.deepEqual(checker.missingFixesFor('99.0.0'), []);
|
|
43
|
+
});
|
|
44
|
+
test('gaps apply by version order, not by listing order', () => {
|
|
45
|
+
const checker = new RespiraVersionChecker(PKG, '8.2.4');
|
|
46
|
+
const at824 = checker.missingFixesFor('8.2.4');
|
|
47
|
+
const at712 = checker.missingFixesFor('7.1.2');
|
|
48
|
+
// 8.2.4 already HAS the site-refresh fix, so it must not be told it lacks it.
|
|
49
|
+
assert.ok(!at824.join(' ').match(/added on the dashboard do not appear/i), '8.2.4 must not be told it is missing its own fix');
|
|
50
|
+
assert.ok(at712.length > at824.length, 'an older build is missing strictly more');
|
|
51
|
+
});
|
|
52
|
+
test('the notice leads with the missing behaviour when there is any', () => {
|
|
53
|
+
const src = readFileSync(new URL('../../src/server.ts', import.meta.url), 'utf8');
|
|
54
|
+
assert.match(src, /missing behaviour you may be working around right now/, 'the message must lead with consequence, not with the version delta');
|
|
55
|
+
assert.match(src, /missing_fixes: update\.missingFixes/, 'the payload must carry the list');
|
|
56
|
+
});
|
|
57
|
+
test('a refresh that could not run is reported, not silently treated as empty', () => {
|
|
58
|
+
const src = readFileSync(new URL('../../src/server.ts', import.meta.url), 'utf8');
|
|
59
|
+
// Every bail-out branch must record a cause.
|
|
60
|
+
for (const status of ['no_account_token', 'unreachable', 'backend_error', 'disabled']) {
|
|
61
|
+
assert.ok(src.includes(`this.lastSiteRefreshStatus = '${status}'`), `the ${status} branch must record why the refresh produced nothing`);
|
|
62
|
+
}
|
|
63
|
+
// And the success path must clear it, or one failure would poison every
|
|
64
|
+
// later response.
|
|
65
|
+
assert.ok(src.includes("this.lastSiteRefreshStatus = 'ok'"), 'a successful refresh must clear the status');
|
|
66
|
+
});
|
|
67
|
+
test('list_sites explains the silence, and stays quiet when healthy', () => {
|
|
68
|
+
const src = readFileSync(new URL('../../src/server.ts', import.meta.url), 'utf8');
|
|
69
|
+
assert.ok(src.includes('describeSiteRefreshStatus()'), 'list_sites must consult the refresh status');
|
|
70
|
+
assert.ok(src.includes('...(refreshNotice ? { site_refresh: refreshNotice } : {})'), 'the notice must be attached only when there is something to say');
|
|
71
|
+
// A healthy refresh must add no noise, or the signal stops being read.
|
|
72
|
+
assert.match(src, /case 'never_run':\s*\n\s*case 'ok':\s*\n\s*default:\s*\n\s*return null;/, 'ok and never_run must produce no notice');
|
|
73
|
+
// The no-token case is the permanent one, so it must name the fix.
|
|
74
|
+
assert.match(src, /npx @respira\/wordpress-mcp-server --setup/, 'the permanent case must name the action');
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=stale-build-and-refresh-silence.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stale-build-and-refresh-silence.test.js","sourceRoot":"","sources":["../../src/__tests__/stale-build-and-refresh-silence.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-signature.d.ts","sourceRoot":"","sources":["../src/agent-signature.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;CACzD;
|
|
1
|
+
{"version":3,"file":"agent-signature.d.ts","sourceRoot":"","sources":["../src/agent-signature.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;CACzD;AAuCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAQvE;AAuHD,wBAAgB,2BAA2B,IAAI,qBAAqB,CA4BnE;AAiBD,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkB/D"}
|
package/dist/agent-signature.js
CHANGED
|
@@ -43,6 +43,12 @@ function normalizeClientInfoName(raw) {
|
|
|
43
43
|
return n.includes('antigravity') ? 'antigravity' : 'gemini';
|
|
44
44
|
if (n.includes('opencode'))
|
|
45
45
|
return 'opencode';
|
|
46
|
+
// Hermes Agent (Nous Research). It already arrives as a bare "hermes" over
|
|
47
|
+
// stdio, so this branch is not what makes it attributable today: it is what
|
|
48
|
+
// keeps it attributable when the name grows ("Hermes Agent 0.5 (desktop)",
|
|
49
|
+
// "hermes-probe"), instead of minting a new slug per release.
|
|
50
|
+
if (n.includes('hermes'))
|
|
51
|
+
return 'hermes';
|
|
46
52
|
// Unknown but self-declared: keep the sanitized raw name rather than
|
|
47
53
|
// guessing. It surfaces new clients in telemetry instead of hiding them.
|
|
48
54
|
return sanitizeHeaderToken(n);
|
|
@@ -142,6 +148,13 @@ function detectClientFromHeuristics() {
|
|
|
142
148
|
return 'copilot';
|
|
143
149
|
if (fingerprint.includes('gemini'))
|
|
144
150
|
return 'gemini';
|
|
151
|
+
// No hermes branch on purpose. Hermes Agent spawns stdio MCP servers through
|
|
152
|
+
// _build_safe_env(), which copies only PATH/HOME/USER/LANG/LC_ALL/TERM/SHELL/
|
|
153
|
+
// TMPDIR, XDG_*, Windows location vars, and user-declared secrets: no
|
|
154
|
+
// HERMES_* marker survives. It also rewrites argv to
|
|
155
|
+
// `python -m tools.mcp_stdio_watchdog <real command>`, so the ps parent does
|
|
156
|
+
// not name Hermes either. There is nothing real to match on, and inventing a
|
|
157
|
+
// pattern would only mislabel someone else. clientInfo covers it instead.
|
|
145
158
|
return 'unknown';
|
|
146
159
|
}
|
|
147
160
|
function detectVersion(client) {
|