glasspane-mcp 1.3.0 → 1.4.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/dist/audit-session.d.ts +85 -2
- package/dist/dispatch.d.ts +1 -1
- package/dist/engine-client.d.ts +131 -12
- package/dist/http-gateway-cli.js +765 -135
- package/dist/http-gateway.d.ts +91 -3
- package/dist/http-gateway.js +719 -122
- package/dist/index.js +701 -117
- package/dist/project-registry.d.ts +46 -15
- package/dist/tools.d.ts +67 -3
- package/dist/update-state.d.ts +25 -0
- package/package.json +1 -1
- package/schemas/evidence-pack.schema.json +2 -0
package/dist/audit-session.d.ts
CHANGED
|
@@ -33,6 +33,44 @@ export interface LateArrivalOutcome {
|
|
|
33
33
|
/** The trail generation in force at the moment of the decision. */
|
|
34
34
|
generationAtWrite: number;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* How an id got into this trail, per id, in the words a renderer can put beside
|
|
38
|
+
* it. The three values are not adjectives of confidence: they name which
|
|
39
|
+
* mechanism wrote the id, and each one is a fact the session itself observes.
|
|
40
|
+
*
|
|
41
|
+
* - `in-order`: written by a call whose own request held a trail turn, answered
|
|
42
|
+
* by a frame that echoed that request's id. This is the only entry the trail
|
|
43
|
+
* may describe as verified.
|
|
44
|
+
* - `late`: written from a reply that landed after its caller had already been
|
|
45
|
+
* answered, so it had no turn left to write at and took the position
|
|
46
|
+
* {@link recordLateArrival} gives it — behind whatever is still outstanding.
|
|
47
|
+
* The *operation* is the daemon's own; only its place in request order is
|
|
48
|
+
* arranged by arrival.
|
|
49
|
+
* - `late-inferred`: that same late write, for a frame that echoed **no request
|
|
50
|
+
* id** (R10-中2). Which request it answers is then a guess from the daemon's
|
|
51
|
+
* in-order scheduling, not a match, and an id pulled out of it can be filed
|
|
52
|
+
* under the wrong operation. The trail still records it — dropping it would
|
|
53
|
+
* claim "no such operation ran", which is the more dangerous lie for an agent
|
|
54
|
+
* deciding whether to click again — but it must not read as verified, and
|
|
55
|
+
* `gp_recent_reports` has to say so from here rather than from the log line.
|
|
56
|
+
*
|
|
57
|
+
* Producers: `EngineJsonRpcClient`'s `LateReply.attribution` decides between the
|
|
58
|
+
* last two (`echoed-id` -> `late`, `arrival-order` -> `late-inferred`); a caller
|
|
59
|
+
* that passes nothing gets `late`, which is everything this session can see on
|
|
60
|
+
* its own.
|
|
61
|
+
*/
|
|
62
|
+
export type TrailProvenance = "in-order" | "late" | "late-inferred";
|
|
63
|
+
/**
|
|
64
|
+
* The attribution a late arrival carries: the same two literals
|
|
65
|
+
* `engine-client.ts`'s `FrameAttribution` produces, and `unknown` for a caller
|
|
66
|
+
* that cannot say (a wiring that has not read the field yet).
|
|
67
|
+
*/
|
|
68
|
+
export type LateArrivalAttribution = "echoed-id" | "arrival-order" | "unknown";
|
|
69
|
+
/**
|
|
70
|
+
* The sentence the surface that shows the trail puts beside one id. Written here
|
|
71
|
+
* because this is the layer that knows the mechanism; the tool layer renders it.
|
|
72
|
+
*/
|
|
73
|
+
export declare const TRAIL_PROVENANCE_NOTES: Readonly<Record<TrailProvenance, string>>;
|
|
36
74
|
export declare class EvidenceAuditSession {
|
|
37
75
|
private ids;
|
|
38
76
|
private epoch;
|
|
@@ -46,8 +84,44 @@ export declare class EvidenceAuditSession {
|
|
|
46
84
|
* order the turn machinery exists to remove.
|
|
47
85
|
*/
|
|
48
86
|
private openGates;
|
|
49
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* How each id in {@link ids} got here. Keyed by the id, because that is what a
|
|
89
|
+
* reader holds; the map is trimmed with the list and cleared when the trail
|
|
90
|
+
* restarts, so an id can never outlive the provenance that describes it.
|
|
91
|
+
*/
|
|
92
|
+
private readonly provenance;
|
|
93
|
+
/**
|
|
94
|
+
* Non-null while {@link recordLateArrival} holds the trail, and says how the
|
|
95
|
+
* frame being written was attributed. This is what lets a plain
|
|
96
|
+
* `trail.record(result)` inside that callback be filed as a late arrival without
|
|
97
|
+
* the caller having to remember to say so: the write happens inside a window only
|
|
98
|
+
* that method opens, so the mechanism is visible here rather than being a claim
|
|
99
|
+
* somebody must repeat at every call site.
|
|
100
|
+
*/
|
|
101
|
+
private lateWindow;
|
|
102
|
+
/**
|
|
103
|
+
* Record every operationId/evidenceId found in an engine result frame, tagged
|
|
104
|
+
* with the provenance of the path that is recording it (see
|
|
105
|
+
* {@link TrailProvenance}): `in-order` from a call that held its own trail turn,
|
|
106
|
+
* `late`/`late-inferred` from inside {@link recordLateArrival}.
|
|
107
|
+
*/
|
|
50
108
|
record(result: unknown): void;
|
|
109
|
+
/**
|
|
110
|
+
* How one id in this trail got recorded, or null when the trail does not hold
|
|
111
|
+
* it — never recorded, evicted past {@link AUDIT_HISTORY_LIMIT}, or cleared by a
|
|
112
|
+
* {@link restart}. A reader that lists ids for an agent renders
|
|
113
|
+
* {@link TRAIL_PROVENANCE_NOTES} for anything that is not `in-order`: an id
|
|
114
|
+
* pulled from a frame this shell only *guessed* an owner for must not appear
|
|
115
|
+
* beside one it matched by id (R10-中2).
|
|
116
|
+
*
|
|
117
|
+
* Read off the map alone, so the map *is* the record of what the trail holds: an
|
|
118
|
+
* id whose provenance outlives its own entry would come back labelled as if it
|
|
119
|
+
* were still listed, and the eviction and the restart are the two places that
|
|
120
|
+
* would have to remember to delete. `test/engine-client.test.mjs` pins both.
|
|
121
|
+
*/
|
|
122
|
+
provenanceOf(id: string): TrailProvenance | null;
|
|
123
|
+
/** The ids currently filed as inferred; see {@link TrailProvenance}. */
|
|
124
|
+
inferredIds(): string[];
|
|
51
125
|
/**
|
|
52
126
|
* The trail's generation number. Every {@link restart} bumps it.
|
|
53
127
|
*
|
|
@@ -109,8 +183,17 @@ export declare class EvidenceAuditSession {
|
|
|
109
183
|
* decides whether that is acceptable; this method refuses it by default, because
|
|
110
184
|
* "we do not know which attach this belongs to" is not a reason to write into
|
|
111
185
|
* somebody else's trail.
|
|
186
|
+
*
|
|
187
|
+
* `attribution` is how the transport matched that frame to a request, and it is
|
|
188
|
+
* the difference between `late` and `late-inferred` below: a frame that echoed
|
|
189
|
+
* the id the client sent is confirmed, one with no id is filed against the
|
|
190
|
+
* oldest tracked request by the daemon's in-order scheduling (R10-中2). Anything
|
|
191
|
+
* this method writes is a late arrival by construction, so the window opens
|
|
192
|
+
* whether or not the caller can say more — the default `unknown` is what the
|
|
193
|
+
* session can see on its own, and it is *not* `in-order`: an unconfirmed
|
|
194
|
+
* attribution must not read as a verified one.
|
|
112
195
|
*/
|
|
113
|
-
recordLateArrival(apply: (session: EvidenceAuditSession) => void, admittedUnderGeneration?: number): Promise<LateArrivalOutcome>;
|
|
196
|
+
recordLateArrival(apply: (session: EvidenceAuditSession) => void, admittedUnderGeneration?: number, attribution?: LateArrivalAttribution): Promise<LateArrivalOutcome>;
|
|
114
197
|
/**
|
|
115
198
|
* Claim the next position in this session's trail request order, so that
|
|
116
199
|
* overlapping calls mutate and read `ids` in the order they were admitted
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare const MCP_PROTOCOL_VERSION: "2025-06-18";
|
|
|
26
26
|
export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly string[];
|
|
27
27
|
export declare const SERVER_INFO: {
|
|
28
28
|
readonly name: "glasspane-mcp";
|
|
29
|
-
readonly version: "1.
|
|
29
|
+
readonly version: "1.4.0";
|
|
30
30
|
};
|
|
31
31
|
export declare const PARSE_ERROR = -32700;
|
|
32
32
|
export declare const INVALID_REQUEST = -32600;
|
package/dist/engine-client.d.ts
CHANGED
|
@@ -76,8 +76,10 @@ export declare const ENGINE_TIMEOUT_MS = 10000;
|
|
|
76
76
|
* `GP_E_ENGINE_TIMEOUT` and the no-replay remedy, and the request stays
|
|
77
77
|
* registered, so the reply that arrives later is still attributed and written
|
|
78
78
|
* to the log (`onDeadline` -> `reportLateReply`). Nothing is discarded by
|
|
79
|
-
* answering early
|
|
80
|
-
*
|
|
79
|
+
* answering early — the only thing that ends that registration is the transport
|
|
80
|
+
* going away, and `teardown` logs the tracked requests it drops when it does —
|
|
81
|
+
* which is what makes the multi-minute deadlines above safe to cap: they
|
|
82
|
+
* describe the daemon, not the caller's wait.
|
|
81
83
|
*/
|
|
82
84
|
export declare const CALLER_VISIBLE_CEILING_MS = 50000;
|
|
83
85
|
/**
|
|
@@ -126,6 +128,39 @@ export declare const ENGINE_DEADLINES_MS: {
|
|
|
126
128
|
};
|
|
127
129
|
/** Fixed part of an ffwd restore deadline (per-step cost is `act` above). */
|
|
128
130
|
export declare const RESTORE_BASE_DEADLINE_MS = 30000;
|
|
131
|
+
/**
|
|
132
|
+
* The single answer to "will re-issuing this method perform a change the daemon may
|
|
133
|
+
* already have carried out?". Exported because two other layers have to ask it —
|
|
134
|
+
* `tools.ts` when it rewrites an oversized reply, `http-gateway.ts` when it answers
|
|
135
|
+
* a curl caller — and a membership list copied into each of them is how the advice
|
|
136
|
+
* for one tool drifts back into "just retry the click". A caller that wants the
|
|
137
|
+
* *reason* per method owns that text; membership comes from here.
|
|
138
|
+
*/
|
|
139
|
+
export declare function isReplayUnsafeMethod(method: string): boolean;
|
|
140
|
+
/** The non-replayable methods, for gates that must see the set change on purpose. */
|
|
141
|
+
export declare const REPLAY_UNSAFE_METHOD_NAMES: readonly string[];
|
|
142
|
+
/**
|
|
143
|
+
* Methods whose re-issue writes daemon state the *caller* depends on, even though
|
|
144
|
+
* replaying them is not unsafe in the sense {@link REPLAY_UNSAFE_REASONS} means
|
|
145
|
+
* (no operation of the user's is performed a second time). They are the other half
|
|
146
|
+
* of the answer to "may this be narrowed by sending it again?", and they are kept
|
|
147
|
+
* apart from the set above because `test/probe-liveness.test.mjs` pins that set's
|
|
148
|
+
* membership as the no-replay judgement.
|
|
149
|
+
*
|
|
150
|
+
* `snapshot` is here because of a ring, not of a click: `storeSnapshot`
|
|
151
|
+
* (`engine/Sources/GlassPaneEngine/EngineCore.swift:2070`) appends the new
|
|
152
|
+
* baseline and then trims oldest-first to `EngineCore.snapshotHistoryLimit` (the
|
|
153
|
+
* same file, line 25 — 8 entries). A `gp_snapshot` whose caller timed out may
|
|
154
|
+
* therefore have been carried out already, and every re-send — "with a smaller
|
|
155
|
+
* maxDepth", which is what the tool layer used to say — adds another entry that can
|
|
156
|
+
* evict the baseline the caller's *own earlier* snapshot returned, the one
|
|
157
|
+
* `gp_restore {snapshotId}` needs later. Evicting a caller's evidence to fetch a
|
|
158
|
+
* cheaper copy of it is not narrowing.
|
|
159
|
+
*
|
|
160
|
+
* `attach` writes state too, and is already a replay-unsafe method; `tools.ts`
|
|
161
|
+
* unions the two lists rather than repeating names here.
|
|
162
|
+
*/
|
|
163
|
+
export declare const STATE_CHANGING_ON_RETRY_METHOD_NAMES: readonly string[];
|
|
129
164
|
/** Deadline for one request; `restore` scales with its replayed step count. */
|
|
130
165
|
export declare function engineDeadlineMs(method: string, params?: Record<string, unknown>): number;
|
|
131
166
|
/** When the caller gets its answer for `method`: its deadline, capped. */
|
|
@@ -209,15 +244,42 @@ export type ShellSurface = "mcp" | "http";
|
|
|
209
244
|
export declare function lateReplyRoute(surface: ShellSurface): string;
|
|
210
245
|
/**
|
|
211
246
|
* Remedy for a deadline overrun: wait and measure, never restart on a probe that
|
|
212
|
-
* merely went unanswered, and never replay a
|
|
247
|
+
* merely went unanswered, and never replay a request whose re-issue has a side
|
|
248
|
+
* effect.
|
|
213
249
|
*/
|
|
214
250
|
export declare function slowEngineRemedy(method: string, surface?: ShellSurface): string;
|
|
251
|
+
/** Log ceiling for a frame body; excess is marked, never silently cut. */
|
|
252
|
+
export declare const LOG_BODY_CHARS: number;
|
|
253
|
+
/**
|
|
254
|
+
* How a reply frame was matched to the request it is reported for.
|
|
255
|
+
*
|
|
256
|
+
* `echoed-id` is the frame carrying the request id this client sent and the
|
|
257
|
+
* lookup hitting; that is the only attribution this transport can *confirm*.
|
|
258
|
+
* `arrival-order` is the daemon's id-less answer (it loses the id on the paths
|
|
259
|
+
* that could not parse or could not serialize the frame), matched to the oldest
|
|
260
|
+
* request still tracked because the daemon answers in the order it received
|
|
261
|
+
* them — an inference from scheduling, never a confirmation, so anything written
|
|
262
|
+
* from it into an evidence trail has to carry that caveat with it
|
|
263
|
+
* (`EvidenceAuditSession.provenanceOf`).
|
|
264
|
+
*
|
|
265
|
+
* Spelled as the same two literals `audit-session.ts` accepts for the same
|
|
266
|
+
* reason: the transport is the only thing that sees the frame, and the session is
|
|
267
|
+
* the only thing that decides what the trail claims about an id.
|
|
268
|
+
*/
|
|
269
|
+
export type FrameAttribution = "echoed-id" | "arrival-order";
|
|
215
270
|
/** One late engine reply, as handed to {@link EngineJsonRpcClient.onLateReply}. */
|
|
216
271
|
export interface LateReply {
|
|
217
272
|
method: string;
|
|
218
273
|
result: unknown;
|
|
219
274
|
/** Whatever `call()` was given; see {@link Pending.correlation}. */
|
|
220
275
|
correlation?: number;
|
|
276
|
+
/**
|
|
277
|
+
* Whether this reply's *request* was confirmed by an echoed id or inferred
|
|
278
|
+
* from arrival order; see {@link FrameAttribution}. A sink that records this
|
|
279
|
+
* body into a trail must not present an `arrival-order` frame as a confirmed
|
|
280
|
+
* one, because the method it is filed under is a guess.
|
|
281
|
+
*/
|
|
282
|
+
attribution: FrameAttribution;
|
|
221
283
|
}
|
|
222
284
|
/**
|
|
223
285
|
* Request/response association over a line transport. Kept independent of
|
|
@@ -237,11 +299,27 @@ export declare class EngineJsonRpcClient {
|
|
|
237
299
|
private readonly callerCeilingMs;
|
|
238
300
|
/** Whom a timeout's remedy is being written for; see {@link ShellSurface}. */
|
|
239
301
|
private readonly surface;
|
|
240
|
-
|
|
241
|
-
|
|
302
|
+
/**
|
|
303
|
+
* Every mutable piece of this client's state, in one object that is *never*
|
|
304
|
+
* reassigned through `this`.
|
|
305
|
+
*
|
|
306
|
+
* That is not a style choice (R10-中4). `tools.ts` hands one tool an engine view
|
|
307
|
+
* built with `Object.create(engine)`, which puts the real client on the shadow's
|
|
308
|
+
* prototype chain so the shadow shares its map, its sinks and its transport — and
|
|
309
|
+
* any method that runs with the *shadow* as `this` then writes its field on the
|
|
310
|
+
* shadow, because assigning a property that exists only on the prototype creates
|
|
311
|
+
* an own property instead of touching the prototype's. `pending` survived that
|
|
312
|
+
* only because it is a `Map` that gets mutated rather than reassigned. A
|
|
313
|
+
* `this.closed = true` did not: it marked the shadow closed and left the client
|
|
314
|
+
* sending requests on a transport it had been told to end. Reading state out of
|
|
315
|
+
* this box still resolves through the chain, and writing a property of it mutates
|
|
316
|
+
* the one box the client itself holds.
|
|
317
|
+
*
|
|
318
|
+
* `test/engine-client.test.mjs` closes the *shadow* and asserts the client behind
|
|
319
|
+
* it is closed, which is the observable form of the whole rule.
|
|
320
|
+
*/
|
|
321
|
+
private readonly state;
|
|
242
322
|
private readonly pending;
|
|
243
|
-
private noteHandler;
|
|
244
|
-
private lateReplyHandler;
|
|
245
323
|
constructor(io: LineIo, timeoutMs?: number | undefined, identity?: EngineIdentityExpectation | null,
|
|
246
324
|
/**
|
|
247
325
|
* Ceiling on the caller's wait (B-02). Injectable because a test cannot
|
|
@@ -271,6 +349,13 @@ export declare class EngineJsonRpcClient {
|
|
|
271
349
|
* the eviction note says when something fell outside it.
|
|
272
350
|
*/
|
|
273
351
|
onLateReply(handler: (reply: LateReply) => void): void;
|
|
352
|
+
/**
|
|
353
|
+
* Whether {@link close} has ended this client. Public because the answer has to
|
|
354
|
+
* be readable *through* the `Object.create` shadow the tool layer builds: a
|
|
355
|
+
* shadow that was closed has to report the client behind it closed, otherwise
|
|
356
|
+
* "closed" is a property of whichever object happened to receive the call.
|
|
357
|
+
*/
|
|
358
|
+
isClosed(): boolean;
|
|
274
359
|
/**
|
|
275
360
|
* Send a request and await its matching response frame. `correlation` is
|
|
276
361
|
* echoed to {@link onLateReply} if this request is answered after its caller
|
|
@@ -293,14 +378,37 @@ export declare class EngineJsonRpcClient {
|
|
|
293
378
|
private settleFromFrame;
|
|
294
379
|
/**
|
|
295
380
|
* A frame over the cap: attribute it to the request it belonged to — from
|
|
296
|
-
* the retained head when the daemon echoed an id, else by arrival order
|
|
297
|
-
*
|
|
381
|
+
* the retained head when the daemon echoed an id, else by arrival order, which
|
|
382
|
+
* is the oldest *tracked* request for the same reason the id-less frame path
|
|
383
|
+
* uses it (R9-高1) — and fail that one call only.
|
|
298
384
|
*/
|
|
299
385
|
private failOversizedReply;
|
|
300
386
|
private engineError;
|
|
301
|
-
/**
|
|
302
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Oldest request this client still tracks, settled ones included: that is the
|
|
389
|
+
* request the daemon's arrival-ordered answer belongs to. `Map` iteration is
|
|
390
|
+
* insertion order, which is the order the requests went out.
|
|
391
|
+
*
|
|
392
|
+
* An entry whose id-less answer has already been taken (`arrivalOrderConsumed`)
|
|
393
|
+
* is passed over for *this* purpose while staying in the map for the echoed-id
|
|
394
|
+
* lookup: the guess has been spent once and may not be spent again on the next
|
|
395
|
+
* id-less frame, and the request it was spent on is still the request whose real
|
|
396
|
+
* reply can arrive with its id (finding 中-4).
|
|
397
|
+
*/
|
|
398
|
+
private oldestTracked;
|
|
399
|
+
/**
|
|
400
|
+
* Route the reply that lands after its caller was already answered. `inferred`
|
|
401
|
+
* marks an attribution made by arrival order rather than by a matching id, and
|
|
402
|
+
* says so in the note — and hands the same distinction to the sink, which is
|
|
403
|
+
* the only place the caveat can travel with the body it records (R10-中2: an
|
|
404
|
+
* id-less result is a *guess about which request this is the answer to*, and a
|
|
405
|
+
* trail that files it without that note presents a guessed frame as a verified
|
|
406
|
+
* one). What the transport may not do is claim the sink recorded anything: it
|
|
407
|
+
* cannot see whether the trail accepted the frame or refused it for belonging to
|
|
408
|
+
* a superseded attach.
|
|
409
|
+
*/
|
|
303
410
|
private reportLateReply;
|
|
411
|
+
private reportSinkFailure;
|
|
304
412
|
private resolve;
|
|
305
413
|
private reject;
|
|
306
414
|
/**
|
|
@@ -309,7 +417,17 @@ export declare class EngineJsonRpcClient {
|
|
|
309
417
|
* longer answer it, and keep it after a timeout for the late reply.
|
|
310
418
|
*/
|
|
311
419
|
private complete;
|
|
312
|
-
/**
|
|
420
|
+
/**
|
|
421
|
+
* Reject everything outstanding and drop every handle. The settled entries go
|
|
422
|
+
* too, and that has to be reported: each of them was answered with "the
|
|
423
|
+
* request stays registered" (`onDeadline`) and with {@link lateReplyRoute}'s
|
|
424
|
+
* promise that its reply is attributed when it lands. This map is what that
|
|
425
|
+
* promise was standing on, so clearing it silently leaves an agent reading the
|
|
426
|
+
* log for an operation that really ran, finding nothing, and unable to tell
|
|
427
|
+
* "this shell stopped tracking it" from "it never happened" — the difference
|
|
428
|
+
* that decides whether it re-performs an `act` on the user's screen (R9-中1).
|
|
429
|
+
* The reverse mutation is to delete this note and keep the silent clear.
|
|
430
|
+
*/
|
|
313
431
|
private teardown;
|
|
314
432
|
private report;
|
|
315
433
|
/**
|
|
@@ -321,6 +439,7 @@ export declare class EngineJsonRpcClient {
|
|
|
321
439
|
private handshake;
|
|
322
440
|
private verifyIdentity;
|
|
323
441
|
}
|
|
442
|
+
export declare function truncate(text: string): string;
|
|
324
443
|
/** How a transport stream is opened; injectable so tests need no socket. */
|
|
325
444
|
export type EngineTransportOpener = (socketPath: string) => Duplex;
|
|
326
445
|
/**
|