@mmnto/mcp 1.113.1 → 1.115.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.
@@ -15,6 +15,7 @@
15
15
  * All writes are fire-and-forget: a ledger failure must NEVER break the tool
16
16
  * call. Telemetry is a sensor, not an actuator (lesson-b1bae311).
17
17
  */
18
+ import type { SelectionCandidate, SelectionContextKey } from '@mmnto/totem';
18
19
  /**
19
20
  * Emit a single `mcp_call` ledger event for the given activity.
20
21
  *
@@ -41,5 +42,21 @@ export declare function logMcpCall(activityName: string): Promise<void>;
41
42
  * Zero hits still counts — the query RAN, and the agent saw its result. The
42
43
  * distinction is "did a corpus query execute", not "did it find something".
43
44
  */
45
+ /**
46
+ * Emit the `search_knowledge` selection manifest (mmnto-ai/totem#2468 M1).
47
+ *
48
+ * Pure sensor: called only after a search actually produced a selection
49
+ * outcome (the same "did it run" boundary `logCorpusQuery` draws — isError
50
+ * outages emit nothing; the `mcp_call` denominator row makes that absence a
51
+ * recorded one, never a silent skip). Failures surface through the search log
52
+ * under the `internal:` idiom — this package speaks MCP over stdio, so
53
+ * stderr/stdout are off-limits by styleguide.
54
+ */
55
+ export declare function logSelectionManifest(input: {
56
+ context: Partial<Record<SelectionContextKey, string | number | boolean | null>>;
57
+ universe: string;
58
+ candidates: SelectionCandidate[];
59
+ warnings: string[];
60
+ }): Promise<void>;
44
61
  export declare function logCorpusQuery(): Promise<void>;
45
62
  //# sourceMappingURL=ledger-writer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-writer.d.ts","sourceRoot":"","sources":["../src/ledger-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BpE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA0BpD"}
1
+ {"version":3,"file":"ledger-writer.d.ts","sourceRoot":"","sources":["../src/ledger-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAe,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAWzF;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BpE;AAED;;;;;;;;;;;;;;;GAeG;AACH;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDhB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA0BpD"}
@@ -16,7 +16,7 @@
16
16
  * call. Telemetry is a sensor, not an actuator (lesson-b1bae311).
17
17
  */
18
18
  import * as path from 'node:path';
19
- import { appendLedgerEvent, readSessionId, senseCorpusQuery } from '@mmnto/totem';
19
+ import { appendLedgerEvent, readSessionId, senseCorpusQuery, senseSelectionManifest, } from '@mmnto/totem';
20
20
  import { getContext } from './context.js';
21
21
  import { logSearch } from './search-log.js';
22
22
  /**
@@ -72,6 +72,62 @@ export async function logMcpCall(activityName) {
72
72
  * Zero hits still counts — the query RAN, and the agent saw its result. The
73
73
  * distinction is "did a corpus query execute", not "did it find something".
74
74
  */
75
+ /**
76
+ * Emit the `search_knowledge` selection manifest (mmnto-ai/totem#2468 M1).
77
+ *
78
+ * Pure sensor: called only after a search actually produced a selection
79
+ * outcome (the same "did it run" boundary `logCorpusQuery` draws — isError
80
+ * outages emit nothing; the `mcp_call` denominator row makes that absence a
81
+ * recorded one, never a silent skip). Failures surface through the search log
82
+ * under the `internal:` idiom — this package speaks MCP over stdio, so
83
+ * stderr/stdout are off-limits by styleguide.
84
+ */
85
+ export async function logSelectionManifest(input) {
86
+ // totem-context: fire-and-forget telemetry; failure must not propagate
87
+ try {
88
+ const { projectRoot, config } = await getContext();
89
+ const totemDir = path.join(projectRoot, config.totemDir);
90
+ // Best-effort emitting-package version (leg round 1, N-13 — row parity
91
+ // with the orient/hook emitters). `../package.json` resolves to
92
+ // packages/mcp/package.json from BOTH src/ and dist/ layouts.
93
+ const rowWarnings = [...input.warnings];
94
+ let emitterVersion;
95
+ try {
96
+ const { createRequire } = await import('node:module');
97
+ emitterVersion = createRequire(import.meta.url)('../package.json')
98
+ .version;
99
+ // totem-context: version is a best-effort enrichment of the manifest row; resolution failure must never cost the row (Tenet 13) — but it lands on the row's accounting channel rather than vanishing (PR #2625 re-review, greptile residual: this emitter had the void-err half only).
100
+ }
101
+ catch (err) {
102
+ rowWarnings.push(`cli_version unavailable: ${err instanceof Error ? err.message : String(err)}`);
103
+ }
104
+ senseSelectionManifest({
105
+ totemDir,
106
+ emitter: 'search_knowledge',
107
+ context: input.context,
108
+ universe: input.universe,
109
+ candidates: input.candidates,
110
+ warnings: rowWarnings,
111
+ ...(emitterVersion !== undefined && { cliVersion: emitterVersion }),
112
+ }, (msg) => {
113
+ logSearch({
114
+ timestamp: new Date().toISOString(),
115
+ query: 'internal:selection-manifest',
116
+ resultCount: 0,
117
+ durationMs: 0,
118
+ topScore: null,
119
+ error: msg,
120
+ });
121
+ });
122
+ // totem-context: fire-and-forget telemetry; failure must not propagate
123
+ }
124
+ catch (err) {
125
+ // intentional swallow — telemetry is a sensor (lesson-b1bae311); failing to
126
+ // record a selection manifest must not crash the MCP server. The absence
127
+ // stays recorded via the mcp_call denominator row (#2468 design).
128
+ void err;
129
+ }
130
+ }
75
131
  export async function logCorpusQuery() {
76
132
  // totem-context: fire-and-forget telemetry; failure must not propagate
77
133
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-writer.js","sourceRoot":"","sources":["../src/ledger-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAElF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,YAAoB;IACnD,0EAA0E;IAC1E,kEAAkE;IAClE,yEAAyE;IACzE,kEAAkE;IAClE,oCAAoC;IACpC,uEAAuE;IACvE,IAAI,CAAC;QACH,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,UAAU,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QAE1C,MAAM,KAAK,GAAgB;YACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,IAAI,EAAE,UAAU;YAChB,aAAa,EAAE,YAAY;YAC3B,MAAM,EAAE,KAAK;YACb,aAAa,EAAE,EAAE;YACjB,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;SAC1D,CAAC;QAEF,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACnC,uEAAuE;IACzE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iEAAiE;QACjE,qEAAqE;QACrE,KAAK,GAAG,CAAC;IACX,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,uEAAuE;IACvE,IAAI,CAAC;QACH,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,UAAU,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzD,gBAAgB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;YACjF,0EAA0E;YAC1E,2EAA2E;YAC3E,2EAA2E;YAC3E,uEAAuE;YACvE,uEAAuE;YACvE,SAAS,CAAC;gBACR,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,KAAK,EAAE,2BAA2B;gBAClC,WAAW,EAAE,CAAC;gBACd,UAAU,EAAE,CAAC;gBACb,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,GAAG;aACX,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,uEAAuE;IACzE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,4EAA4E;QAC5E,2DAA2D;QAC3D,KAAK,GAAG,CAAC;IACX,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"ledger-writer.js","sourceRoot":"","sources":["../src/ledger-writer.ts"],"names":[],"mappings":"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"}
@@ -128,4 +128,67 @@ describe('logCorpusQuery (mmnto-ai/totem#2510)', () => {
128
128
  await expect(logCorpusQuery()).resolves.toBeUndefined();
129
129
  });
130
130
  });
131
+ describe('logSelectionManifest (mmnto-ai/totem#2468)', () => {
132
+ const input = {
133
+ context: { query: 'q', status: 'ok' },
134
+ universe: 'store-returned-pool maxResults=5',
135
+ candidates: [
136
+ {
137
+ id: 'a.ts',
138
+ disposition: 'selected',
139
+ reason: 'returned rank=1',
140
+ bytes: 4,
141
+ approxTokens: 1,
142
+ // Measured fields travel together (schema invariant, PR #2625 CR
143
+ // round) — a bytes-without-fingerprint fixture is now a contract
144
+ // violation, as it should be.
145
+ fingerprint: '0123456789abcdef',
146
+ },
147
+ ],
148
+ warnings: [],
149
+ };
150
+ it('writes a schema-valid row to selection-manifests.ndjson end-to-end', async () => {
151
+ fs.mkdirSync(path.join(tmpDir, '.totem'), { recursive: true });
152
+ mockContext();
153
+ const { logSelectionManifest } = await import('./ledger-writer.js');
154
+ await logSelectionManifest(input);
155
+ const rows = readRows(path.join(tmpDir, '.totem', 'ledger', 'selection-manifests.ndjson'));
156
+ expect(rows).toHaveLength(1);
157
+ expect(rows[0]).toMatchObject({
158
+ schemaVersion: 1,
159
+ emitter: 'search_knowledge',
160
+ universe: 'store-returned-pool maxResults=5',
161
+ });
162
+ expect(rows[0].costBasis).toEqual({
163
+ bytes: 'utf8-length',
164
+ approxTokens: 'ceil(bytes/4) approximation',
165
+ });
166
+ // Emitting-package version rides the row (leg round 2, finding 4 — the
167
+ // compile.ts precedent names "regressions that strip cli_version" as a
168
+ // known class; this guards the seam).
169
+ expect(String(rows[0].cli_version)).toMatch(/^\d+\.\d+\.\d+/);
170
+ // The events.ndjson stream is untouched — the sidecar ruling (#2468 OQ1).
171
+ expect(fs.existsSync(path.join(tmpDir, '.totem', 'ledger', 'events.ndjson'))).toBe(false);
172
+ });
173
+ it('does not throw when getContext fails', async () => {
174
+ vi.doMock('./context.js', () => ({
175
+ getContext: async () => {
176
+ throw new Error('context load failed');
177
+ },
178
+ }));
179
+ const { logSelectionManifest } = await import('./ledger-writer.js');
180
+ await expect(logSelectionManifest(input)).resolves.toBeUndefined();
181
+ });
182
+ it('never throws on a contract-invalid row — the sense wrapper absorbs it, nothing persists', async () => {
183
+ fs.mkdirSync(path.join(tmpDir, '.totem'), { recursive: true });
184
+ mockContext();
185
+ const { logSelectionManifest } = await import('./ledger-writer.js');
186
+ const poisoned = {
187
+ ...input,
188
+ candidates: [{ ...input.candidates[0], reason: '' }],
189
+ };
190
+ await expect(logSelectionManifest(poisoned)).resolves.toBeUndefined();
191
+ expect(fs.existsSync(path.join(tmpDir, '.totem', 'ledger', 'selection-manifests.ndjson'))).toBe(false);
192
+ });
193
+ });
131
194
  //# sourceMappingURL=ledger-writer.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-writer.test.js","sourceRoot":"","sources":["../src/ledger-writer.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ledger-writer.test.js","sourceRoot":"","sources":["../src/ledger-writer.test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEzE,IAAI,MAAc,CAAC;AAEnB,UAAU,CAAC,GAAG,EAAE;IACd,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACrE,EAAE,CAAC,YAAY,EAAE,CAAC;AACpB,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,sCAAsC;AACtC,SAAS,QAAQ,CAAC,UAAkB;IAClC,OAAO,EAAE;SACN,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC;SACjC,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAA4B,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,WAAW;IAClB,mEAAmE;IACnE,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/B,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YACvB,WAAW,EAAE,MAAM;YACnB,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;SAC/B,CAAC;KACH,CAAC,CAAC,CAAC;AACN,CAAC;AAED,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1D,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;QAErC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAC1E,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE7C,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAClC,yEAAyE;QACzE,wEAAwE;QACxE,wEAAwE;QACxE,uDAAuD;QACvD,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAE7B,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACxB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1D,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;QAErC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;QAC9E,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,SAAS,GAAG,sCAAsC,CAAC;QACzD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACxD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAE1E,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1D,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;QAErC,MAAM,KAAK,GAAG,EAAE;aACb,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC;aAC5D,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1D,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;QAErC,MAAM,KAAK,GAAG,EAAE;aACb,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC;aAC7E,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,aAAa,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;YAC/B,UAAU,EAAE,KAAK,IAAI,EAAE;gBACrB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACzC,CAAC;SACF,CAAC,CAAC,CAAC;QACJ,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1D,iDAAiD;QACjD,MAAM,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1D,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;QACrC,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;QAErC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;QAC9E,0EAA0E;QAC1E,wEAAwE;QACxE,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;QAClE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;IACpD,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,qEAAqE;QACrE,+DAA+D;QAC/D,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC9D,MAAM,cAAc,EAAE,CAAC;QAEvB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;QAC9E,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,CAAE,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;YAC/B,UAAU,EAAE,KAAK,IAAI,EAAE;gBACrB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACzC,CAAC;SACF,CAAC,CAAC,CAAC;QACJ,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC9D,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4CAA4C,EAAE,GAAG,EAAE;IAC1D,MAAM,KAAK,GAAG;QACZ,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;QACrC,QAAQ,EAAE,kCAAkC;QAC5C,UAAU,EAAE;YACV;gBACE,EAAE,EAAE,MAAM;gBACV,WAAW,EAAE,UAAmB;gBAChC,MAAM,EAAE,iBAAiB;gBACzB,KAAK,EAAE,CAAC;gBACR,YAAY,EAAE,CAAC;gBACf,iEAAiE;gBACjE,iEAAiE;gBACjE,8BAA8B;gBAC9B,WAAW,EAAE,kBAAkB;aAChC;SACF;QACD,QAAQ,EAAE,EAAE;KACb,CAAC;IAEF,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACpE,MAAM,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAElC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC,CAAC;QAC3F,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,aAAa,CAAC;YAC7B,aAAa,EAAE,CAAC;YAChB,OAAO,EAAE,kBAAkB;YAC3B,QAAQ,EAAE,kCAAkC;SAC7C,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC;YACjC,KAAK,EAAE,aAAa;YACpB,YAAY,EAAE,6BAA6B;SAC5C,CAAC,CAAC;QACH,uEAAuE;QACvE,uEAAuE;QACvE,sCAAsC;QACtC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC/D,0EAA0E;QAC1E,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;YAC/B,UAAU,EAAE,KAAK,IAAI,EAAE;gBACrB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACzC,CAAC;SACF,CAAC,CAAC,CAAC;QACJ,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACpE,MAAM,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yFAAyF,EAAE,KAAK,IAAI,EAAE;QACvG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG;YACf,GAAG,KAAK;YACR,UAAU,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;SACtD,CAAC;QACF,MAAM,MAAM,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QACtE,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC,CAAC,CAAC,IAAI,CAC7F,KAAK,CACN,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -21,5 +21,42 @@ export declare function formatRetrievalEnvelope(params: {
21
21
  floor: number;
22
22
  hits: number;
23
23
  }): string;
24
+ /**
25
+ * Per-process accumulation behind the `<size-disclosure>` envelope (#2600).
26
+ * The basis is the MCP server process's lifetime, declared in-band via the
27
+ * envelope's `scope="server-process"`: totals survive conversation clears
28
+ * and compactions, and a client running multiple totem MCP servers sees
29
+ * independent per-server totals. That is the only scope measurable
30
+ * UNCONDITIONALLY at this seam — `TOTEM_SESSION_ID`, when a harness sets it
31
+ * (see search-log.ts), could key a finer scope; deciding that is #2604.
32
+ * Counts non-error responses only (error bodies are bounded provider/store
33
+ * text, never corpus content). Exported with {@link withSizeDisclosure} so
34
+ * unit tests can drive and reset the accumulator directly.
35
+ */
36
+ export declare const sessionPayload: {
37
+ chars: number;
38
+ calls: number;
39
+ };
40
+ /**
41
+ * Count the response toward the session totals and, when THIS response
42
+ * crosses `contextWarningThreshold`, append a self-closing measurement
43
+ * envelope (house style of `<index-meta>` / `<retrieval-envelope>`):
44
+ *
45
+ * `<size-disclosure chars="…" approxTokens="…" sessionChars="…" sessionCalls="…" scope="server-process" />`
46
+ *
47
+ * A measurement with NO risk claim: this seam never sees the consumer's
48
+ * model, window size, or occupancy, so the context-pressure warning it
49
+ * replaces was denominator-blind by construction (live-falsified at 15%
50
+ * occupancy on a 1M-window seat, #2600). The weighing judgment lives with
51
+ * the only party holding the denominator — the consumer, per the generated
52
+ * CLAUDE.md Context Management Guardrail (reflex v10, #2599). `chars` counts
53
+ * the delivered payload BEFORE this envelope (the envelope never counts
54
+ * itself, in the attribute or the accumulator). Applied once, at the
55
+ * handler's finalize seam AFTER the first-call warning prepend — measuring
56
+ * earlier understated `chars` on exactly the call a first-time consumer
57
+ * reads. Attribute values are numerals and a fixed token, so no XML
58
+ * escaping is required.
59
+ */
60
+ export declare function withSizeDisclosure(text: string, thresholdChars: number): string;
24
61
  export declare function registerSearchKnowledge(server: McpServer): void;
25
62
  //# sourceMappingURL=search-knowledge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-knowledge.d.ts","sourceRoot":"","sources":["../../src/tools/search-knowledge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA0CzE;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAkWD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,MAAM,EAAE,IAAI,GAAG,gBAAgB,GAAG,OAAO,CAAC;IAC1C,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CAMT;AAgcD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAoO/D"}
1
+ {"version":3,"file":"search-knowledge.d.ts","sourceRoot":"","sources":["../../src/tools/search-knowledge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmDzE;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAyWD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,MAAM,EAAE,IAAI,GAAG,gBAAgB,GAAG,OAAO,CAAC;IAC1C,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CAMT;AA2gBD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc;;;CAAyB,CAAC;AAYrD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAU/E;AAmCD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0P/D"}
@@ -1,8 +1,8 @@
1
1
  import * as path from 'node:path';
2
2
  import { z } from 'zod';
3
- import { ContentTypeSchema, DEFAULT_SEARCH_RELEVANCE_FLOOR } from '@mmnto/totem';
3
+ import { buildMeasuredCandidate, ContentTypeSchema, DEFAULT_SEARCH_RELEVANCE_FLOOR, } from '@mmnto/totem';
4
4
  import { getContext, reconnectStore } from '../context.js';
5
- import { logCorpusQuery, logMcpCall } from '../ledger-writer.js';
5
+ import { logCorpusQuery, logMcpCall, logSelectionManifest } from '../ledger-writer.js';
6
6
  import { logSearch, setLogDir } from '../search-log.js';
7
7
  import { extractIndexState } from '../state-extractors.js';
8
8
  import { formatIndexEnvelope, formatSystemWarning, formatXmlResponse } from '../xml-format.js';
@@ -201,7 +201,7 @@ function formatResult(r, index) {
201
201
  *
202
202
  * mmnto/totem#1294 Phase 2 + mmnto/totem#1295 fix.
203
203
  */
204
- async function federatedSearch(query, typeFilter, perStoreLimit, finalLimit, failures, onFtsFallback) {
204
+ async function federatedSearch(query, typeFilter, perStoreLimit, finalLimit, failures, onFtsFallback, overflow) {
205
205
  const { store: primaryStore, linkedStores } = await getContext();
206
206
  // mmnto/totem#1295 GCA HIGH: catch primary failures inside federatedSearch
207
207
  // ONLY when linked stores exist, so a transient primary failure doesn't
@@ -309,6 +309,7 @@ async function federatedSearch(query, typeFilter, perStoreLimit, finalLimit, fai
309
309
  // already comparable since they all come from one store. Skip RRF
310
310
  // normalization to keep the original scores visible to the agent.
311
311
  if (buckets.length === 1) {
312
+ overflow?.push(...primaryResults.slice(finalLimit));
312
313
  return primaryResults.slice(0, finalLimit);
313
314
  }
314
315
  // mmnto/totem#1295 GCA CRITICAL: re-rank via Reciprocal Rank Fusion
@@ -342,6 +343,11 @@ async function federatedSearch(query, typeFilter, perStoreLimit, finalLimit, fai
342
343
  });
343
344
  }
344
345
  reranked.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
346
+ // Selection-manifest visibility (mmnto-ai/totem#2468): the final-limit cut
347
+ // was this pipeline's one SILENT exclusion — fetched, ranked, then dropped
348
+ // with no trace. Report the cut candidates so the emitter can record them;
349
+ // the returned selection is byte-identical with or without the out-param.
350
+ overflow?.push(...reranked.slice(finalLimit));
345
351
  return reranked.slice(0, finalLimit);
346
352
  }
347
353
  /**
@@ -399,6 +405,10 @@ async function performSearch(query, typeFilter, maxResults, boundary, minRelevan
399
405
  // `deriveLinkName` strips leading dots so a `.primary/` linked repo would
400
406
  // collide with a reserved key. CR MAJOR catch on round 7.
401
407
  const failures = makeFailureLog();
408
+ // Selection-manifest capture (mmnto-ai/totem#2468): candidates the federation
409
+ // fetched but cut at the final limit — populated only by Case 5, mirroring
410
+ // the `failures` out-param pattern. Purely observational.
411
+ const federationOverflow = [];
402
412
  // ─── Boundary resolution (mmnto/totem#1294 Phase 2) ──
403
413
  //
404
414
  // Order of precedence:
@@ -524,7 +534,7 @@ async function performSearch(query, typeFilter, maxResults, boundary, minRelevan
524
534
  // Pass the `failures` log so the federation path can populate it on
525
535
  // store errors without mutating global state. Primary lives in a
526
536
  // dedicated slot to avoid colliding with linked-store names.
527
- results = await federatedSearch(query, typeFilter, finalLimit, finalLimit, failures, onFtsFallback);
537
+ results = await federatedSearch(query, typeFilter, finalLimit, finalLimit, failures, onFtsFallback, federationOverflow);
528
538
  }
529
539
  // Build the runtime-failures warning (if any) once so we can decide
530
540
  // whether to skip the "no results" early return for warn-only cases.
@@ -619,6 +629,94 @@ async function performSearch(query, typeFilter, maxResults, boundary, minRelevan
619
629
  .filter((x) => typeof x === 'number');
620
630
  const hasRelevanceSignal = relevances.length > 0;
621
631
  const bestRelevance = hasRelevanceSignal ? Math.max(...relevances) : null;
632
+ // ─── Selection-manifest emitter (mmnto-ai/totem#2468 M1) ──
633
+ //
634
+ // One manifest per selection OUTCOME (empty / no_useful_hits / mixed / ok) —
635
+ // the same "did a selection happen" boundary logCorpusQuery draws for the
636
+ // QBD row. isError outages emit nothing: the mcp_call denominator row makes
637
+ // that absence recorded, never silent. Each branch calls this AFTER
638
+ // composing its response text, so emission can never alter the selection
639
+ // (Tenet 13); logSelectionManifest swallows its own failures into the
640
+ // search log.
641
+ //
642
+ // Every candidate here was FETCHED by the policy (returned, withheld at the
643
+ // floor, or cut at the federation final limit), so every row is fully
644
+ // measured — the id-only form exists for policies that never read their
645
+ // exclusions, which this one has none of. `universe` names that boundary:
646
+ // exclusions are recorded over the fetched pool, not the whole index.
647
+ const emitSelectionManifest = async (status, parts) => {
648
+ const candidates = [];
649
+ const manifestWarnings = [];
650
+ const add = (hit, disposition, reason) => {
651
+ const { candidate, warning } = buildMeasuredCandidate({
652
+ id: hit.filePath,
653
+ content: hit.content,
654
+ disposition,
655
+ reason,
656
+ ...(hit.sourceRepo !== undefined && { sourceRepo: hit.sourceRepo }),
657
+ });
658
+ candidates.push(candidate);
659
+ if (warning !== undefined)
660
+ manifestWarnings.push(warning);
661
+ };
662
+ for (const { hit, reason } of parts.selected ?? [])
663
+ add(hit, 'selected', reason);
664
+ for (const { hit, reason } of parts.excluded ?? [])
665
+ add(hit, 'excluded', reason);
666
+ for (const hit of federationOverflow)
667
+ add(hit, 'excluded', 'federation-final-limit overflow');
668
+ const federated = boundary === undefined && linkedStores.size > 0;
669
+ // Store outages are part of the observable-pool accounting (leg round 1,
670
+ // D-3): without them a query where stores errored reads as a FULL pool.
671
+ // Each failed store is named on the row's accounting channel, and the
672
+ // federated universe string carries how many stores actually answered.
673
+ if (failures.primary !== null) {
674
+ manifestWarnings.push(`store failure: primary: ${failures.primary}`);
675
+ }
676
+ for (const [name, msg] of failures.linked.entries()) {
677
+ manifestWarnings.push(`store failure: ${name}: ${msg}`);
678
+ }
679
+ const storesFailed = (failures.primary !== null ? 1 : 0) + failures.linked.size;
680
+ try {
681
+ await logSelectionManifest({
682
+ context: {
683
+ query,
684
+ boundary: boundary ?? null,
685
+ typeFilter: typeFilter ?? null,
686
+ floor: effectiveFloor,
687
+ finalLimit,
688
+ method,
689
+ status,
690
+ storesFailed,
691
+ // Links that never initialized are absent from `linkedStores`, so
692
+ // `answered` alone would read a permanently-broken link as a full
693
+ // pool — the exact misreading D-3 exists to prevent (leg round 2,
694
+ // finding 3). Counted as init-error entries NOT also present in
695
+ // `linkedStores`: that map carries non-fatal startup warnings for
696
+ // stores that still serve (empty stores, name collisions), and
697
+ // counting those would record an answering store as unavailable
698
+ // (PR #2625, greptile P1 + CR — the double-count both bots named).
699
+ storesUnavailable: [...linkedStoreInitErrors.keys()].filter((name) => !linkedStores.has(name)).length,
700
+ },
701
+ universe: federated
702
+ ? `federated-returned-pool perStoreLimit=${finalLimit} stores=${linkedStores.size + 1} answered=${linkedStores.size + 1 - storesFailed}`
703
+ : `store-returned-pool maxResults=${finalLimit}`,
704
+ candidates,
705
+ warnings: manifestWarnings,
706
+ });
707
+ // totem-context: defense-in-depth (leg round 1, D-5) — logSelectionManifest swallows its own failures by contract; this catch guarantees the sensor can never surface as a search failure even if that contract regresses, and the failure stays visible via the search log (never swallowed silently).
708
+ }
709
+ catch (err) {
710
+ logSearch({
711
+ timestamp: new Date().toISOString(),
712
+ query: 'internal:selection-manifest',
713
+ resultCount: 0,
714
+ durationMs: 0,
715
+ topScore: null,
716
+ error: `selection-manifest emit failed: ${err instanceof Error ? err.message : String(err)}`,
717
+ });
718
+ }
719
+ };
622
720
  if (results.length === 0) {
623
721
  if (allFederatedStoresFailed) {
624
722
  return {
@@ -648,6 +746,7 @@ async function performSearch(query, typeFilter, maxResults, boundary, minRelevan
648
746
  retrievalEnvelope: emptyEnvelope,
649
747
  body,
650
748
  }); // totem-ignore mmnto-ai/totem#1294 — composed from system-generated + XML-wrapped pieces
749
+ await emitSelectionManifest('empty', {});
651
750
  return { content: [{ type: 'text', text }] };
652
751
  }
653
752
  // Floor fires ONLY when a real relevance signal exists — a pure-FTS corpus
@@ -693,6 +792,12 @@ async function performSearch(query, typeFilter, maxResults, boundary, minRelevan
693
792
  retrievalEnvelope: belowFloorEnvelope,
694
793
  body,
695
794
  }); // totem-ignore mmnto-ai/totem#1294 — composed from system-generated + XML-wrapped pieces
795
+ await emitSelectionManifest('no_useful_hits', {
796
+ excluded: withheld.map((hit) => ({
797
+ hit,
798
+ reason: `below-relevance-floor floor=${effectiveFloor.toFixed(3)} relevance=${typeof hit.relevance === 'number' ? hit.relevance.toFixed(3) : 'n/a'}`,
799
+ })),
800
+ });
696
801
  return { content: [{ type: 'text', text }] };
697
802
  }
698
803
  // Mixed batch: return the floor-exempt keyword hits, disclose the
@@ -714,6 +819,16 @@ async function performSearch(query, typeFilter, maxResults, boundary, minRelevan
714
819
  retrievalEnvelope: mixedEnvelope,
715
820
  body,
716
821
  }); // totem-ignore mmnto-ai/totem#1294 — composed from system-generated + XML-wrapped pieces
822
+ await emitSelectionManifest('ok', {
823
+ selected: floorExempt.map((hit) => ({
824
+ hit,
825
+ reason: 'floor-exempt keyword hit (no relevance signal)',
826
+ })),
827
+ excluded: withheld.map((hit) => ({
828
+ hit,
829
+ reason: `below-relevance-floor floor=${effectiveFloor.toFixed(3)} relevance=${typeof hit.relevance === 'number' ? hit.relevance.toFixed(3) : 'n/a'}`,
830
+ })),
831
+ });
717
832
  return { content: [{ type: 'text', text }] };
718
833
  }
719
834
  const okEnvelope = formatRetrievalEnvelope({
@@ -725,7 +840,7 @@ async function performSearch(query, typeFilter, maxResults, boundary, minRelevan
725
840
  });
726
841
  const formatted = results.map((r, i) => formatResult(r, i)).join('\n\n---\n\n');
727
842
  const knowledgeBody = formatXmlResponse('knowledge', formatted);
728
- let text = composeResponseText({
843
+ const text = composeResponseText({
729
844
  fallbackWarning,
730
845
  runtimeWarning,
731
846
  staleWarning,
@@ -733,15 +848,62 @@ async function performSearch(query, typeFilter, maxResults, boundary, minRelevan
733
848
  retrievalEnvelope: okEnvelope,
734
849
  body: knowledgeBody,
735
850
  }); // totem-ignore mmnto-ai/totem#1294 — composed from system-generated + XML-wrapped pieces
736
- // Append a system warning when the payload is large enough to risk context pressure
737
- if (text.length > config.contextWarningThreshold) {
738
- text +=
739
- '\n\n' +
740
- formatSystemWarning('You just ingested a large amount of context. You may be at risk of forgetting earlier instructions. ' +
741
- 'Consider warning the user about context pressure and suggest running `totem handoff` to capture mid-session state.');
742
- }
851
+ await emitSelectionManifest('ok', {
852
+ selected: results.map((hit, i) => ({ hit, reason: `returned rank=${i + 1}` })),
853
+ });
743
854
  return { content: [{ type: 'text', text }] };
744
855
  }
856
+ /**
857
+ * Per-process accumulation behind the `<size-disclosure>` envelope (#2600).
858
+ * The basis is the MCP server process's lifetime, declared in-band via the
859
+ * envelope's `scope="server-process"`: totals survive conversation clears
860
+ * and compactions, and a client running multiple totem MCP servers sees
861
+ * independent per-server totals. That is the only scope measurable
862
+ * UNCONDITIONALLY at this seam — `TOTEM_SESSION_ID`, when a harness sets it
863
+ * (see search-log.ts), could key a finer scope; deciding that is #2604.
864
+ * Counts non-error responses only (error bodies are bounded provider/store
865
+ * text, never corpus content). Exported with {@link withSizeDisclosure} so
866
+ * unit tests can drive and reset the accumulator directly.
867
+ */
868
+ export const sessionPayload = { chars: 0, calls: 0 };
869
+ /** The config JSDoc's stated approximation: ~4 chars ≈ 1 token. */
870
+ const APPROX_CHARS_PER_TOKEN = 4;
871
+ /**
872
+ * Fallback threshold when the config cannot be re-read at the finalize seam
873
+ * (practically unreachable after a successful search) — mirrors the
874
+ * `contextWarningThreshold` default in core's config-schema.
875
+ */
876
+ const CONTEXT_WARNING_THRESHOLD_FALLBACK = 40_000;
877
+ /**
878
+ * Count the response toward the session totals and, when THIS response
879
+ * crosses `contextWarningThreshold`, append a self-closing measurement
880
+ * envelope (house style of `<index-meta>` / `<retrieval-envelope>`):
881
+ *
882
+ * `<size-disclosure chars="…" approxTokens="…" sessionChars="…" sessionCalls="…" scope="server-process" />`
883
+ *
884
+ * A measurement with NO risk claim: this seam never sees the consumer's
885
+ * model, window size, or occupancy, so the context-pressure warning it
886
+ * replaces was denominator-blind by construction (live-falsified at 15%
887
+ * occupancy on a 1M-window seat, #2600). The weighing judgment lives with
888
+ * the only party holding the denominator — the consumer, per the generated
889
+ * CLAUDE.md Context Management Guardrail (reflex v10, #2599). `chars` counts
890
+ * the delivered payload BEFORE this envelope (the envelope never counts
891
+ * itself, in the attribute or the accumulator). Applied once, at the
892
+ * handler's finalize seam AFTER the first-call warning prepend — measuring
893
+ * earlier understated `chars` on exactly the call a first-time consumer
894
+ * reads. Attribute values are numerals and a fixed token, so no XML
895
+ * escaping is required.
896
+ */
897
+ export function withSizeDisclosure(text, thresholdChars) {
898
+ sessionPayload.calls += 1;
899
+ sessionPayload.chars += text.length;
900
+ if (text.length <= thresholdChars)
901
+ return text;
902
+ const approxTokens = Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
903
+ return (text +
904
+ `\n\n<size-disclosure chars="${text.length}" approxTokens="${approxTokens}" ` +
905
+ `sessionChars="${sessionPayload.chars}" sessionCalls="${sessionPayload.calls}" scope="server-process" />`);
906
+ }
745
907
  /**
746
908
  * Compose the final response text by stacking diagnostics on top of the
747
909
  * knowledge body. Order (outermost → innermost):
@@ -963,6 +1125,28 @@ export function registerSearchKnowledge(server) {
963
1125
  text: warnings.join('\n\n') + '\n\n' + result.content[0].text, // totem-ignore #1294 — all system-generated + XML-wrapped
964
1126
  };
965
1127
  }
1128
+ // Size disclosure LAST, on the final delivered text (#2600): the
1129
+ // first-call warning prepend above is part of what the consumer
1130
+ // ingests, so measuring any earlier understates `chars` on exactly
1131
+ // the call a first-time consumer reads. Error responses excluded —
1132
+ // bounded error text, never corpus content. getContext is memoized
1133
+ // and already resolved for the search itself; the fallback default
1134
+ // is unreachable in practice.
1135
+ if (result.isError !== true && result.content.length > 0) {
1136
+ let threshold = CONTEXT_WARNING_THRESHOLD_FALLBACK;
1137
+ // totem-context: intentional fallback (#2600) — a context failure after a successful search cannot happen (memoized); the named default keeps the disclosure seam total rather than letting it throw away the response.
1138
+ try {
1139
+ threshold = (await getContext()).config.contextWarningThreshold;
1140
+ // totem-context: intentional fallback (#2600) — see directive above the try; dual placement so the rule fires on either the catch-keyword line or the catch-body line.
1141
+ }
1142
+ catch (err) {
1143
+ void err; // fall through to the named default
1144
+ }
1145
+ result.content[0] = {
1146
+ type: 'text',
1147
+ text: withSizeDisclosure(result.content[0].text, threshold),
1148
+ };
1149
+ }
966
1150
  return result;
967
1151
  }
968
1152
  catch (err) {