bantamkit-mcp 0.26.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/LICENSE +21 -0
- package/README.md +398 -0
- package/assets/contracts/default.yaml +90 -0
- package/assets/evals/devteam/manifest.yaml +351 -0
- package/assets/evals/devteam/repo/HISTORY.md +18 -0
- package/assets/evals/devteam/repo/README.md +12 -0
- package/assets/evals/devteam/repo/docs/architecture.md +17 -0
- package/assets/evals/devteam/repo/docs/runbook.md +10 -0
- package/assets/evals/devteam/repo/issues/142-settlement-timeout.md +23 -0
- package/assets/evals/devteam/repo/patches/0009-retry-budget.patch +38 -0
- package/assets/evals/devteam/repo/src/ledger/__init__.py +3 -0
- package/assets/evals/devteam/repo/src/ledger/config.py +35 -0
- package/assets/evals/devteam/repo/src/ledger/errors.py +13 -0
- package/assets/evals/devteam/repo/src/ledger/posting.py +12 -0
- package/assets/evals/devteam/repo/src/ledger/registry.py +7 -0
- package/assets/evals/devteam/repo/src/ledger/report.py +9 -0
- package/assets/evals/devteam/repo/src/ledger/retry.py +17 -0
- package/assets/evals/devteam/repo/src/ledger/settle.py +16 -0
- package/assets/evals/devteam/repo/src/ledger/validate.py +14 -0
- package/assets/evals/devteam/repo/tests/test_posting.py +13 -0
- package/assets/evals/devteam/repo/tests/test_settle.py +9 -0
- package/assets/evals/devteam/tasks/dt-error-contract.yaml +186 -0
- package/assets/evals/devteam/tasks/dt-handler-map.yaml +183 -0
- package/assets/evals/devteam/tasks/dt-patch-before-after.yaml +182 -0
- package/assets/evals/devteam/tasks/dt-retry-attempts.yaml +181 -0
- package/assets/evals/devteam/tasks/dt-settlement-config.yaml +185 -0
- package/assets/evals/devteam/tasks/dt-symbol-home.yaml +181 -0
- package/assets/evals/devteam/tasks/dt-trace-blame.yaml +182 -0
- package/assets/evals/devteam/tasks/dt-unread-key.yaml +180 -0
- package/assets/evals/document/tasks/doc-large-in-137.yaml +38 -0
- package/assets/evals/document/tasks/doc-large-in-359.yaml +44 -0
- package/assets/evals/document/tasks/doc-large-in-372.yaml +38 -0
- package/assets/evals/document/tasks/doc-large-out-11764.yaml +37 -0
- package/assets/evals/document/tasks/doc-large-out-4137.yaml +37 -0
- package/assets/evals/document/tasks/doc-large-out-8022.yaml +37 -0
- package/assets/evals/document/tasks/doc-small-137.yaml +37 -0
- package/assets/evals/document/tasks/doc-small-261.yaml +37 -0
- package/assets/evals/document/tasks/doc-small-388.yaml +37 -0
- package/assets/evals/fixtures/.gitkeep +0 -0
- package/assets/evals/fixtures/catalog.json +6 -0
- package/assets/evals/perturbations/task-completion.yaml +576 -0
- package/assets/evals/tasks/.gitkeep +0 -0
- package/assets/evals/tasks/extract-contact.yaml +14 -0
- package/assets/evals/tasks/extract-invoice.yaml +14 -0
- package/assets/evals/tasks/extract-order.yaml +15 -0
- package/assets/evals/tasks/extract-schedule.yaml +14 -0
- package/assets/evals/tasks/extract-versions.yaml +17 -0
- package/assets/evals/tasks/nav-prod-port.yaml +84 -0
- package/assets/evals/tasks/nav-release-bundle.yaml +87 -0
- package/assets/evals/tasks/recall-audit-retention.yaml +17 -0
- package/assets/evals/tasks/recall-cache-ttl.yaml +13 -0
- package/assets/evals/tasks/recall-db-port.yaml +17 -0
- package/assets/evals/tasks/recall-deploy.yaml +13 -0
- package/assets/evals/tasks/recall-env-endpoint.yaml +18 -0
- package/assets/evals/tasks/recall-oncall-rotation.yaml +21 -0
- package/assets/evals/tasks/recall-oncall.yaml +13 -0
- package/assets/evals/tasks/recall-org-quota.yaml +18 -0
- package/assets/evals/tasks/recall-owner.yaml +13 -0
- package/assets/evals/tasks/shop-basket-total.yaml +10 -0
- package/assets/evals/tasks/shop-cheapest.yaml +9 -0
- package/assets/evals/tasks/shop-compare.yaml +9 -0
- package/assets/evals/tasks/shop-gadget-value.yaml +9 -0
- package/assets/evals/tasks/shop-stock-total.yaml +9 -0
- package/assets/evals/tasks/shop-total.yaml +9 -0
- package/assets/profiles/default.yaml +31 -0
- package/assets/profiles/patient.yaml +31 -0
- package/assets/rubrics/.gitkeep +0 -0
- package/assets/rubrics/code-quality.yaml +20 -0
- package/assets/rubrics/grounded-completion.yaml +37 -0
- package/assets/rubrics/task-completion.yaml +28 -0
- package/assets/schemas/shiftwork-checkpoint.json +188 -0
- package/assets/skills/.gitkeep +0 -0
- package/assets/skills/file-graph.md +7 -0
- package/assets/skills/memory.md +35 -0
- package/assets/tools/.gitkeep +0 -0
- package/assets/tools/bantamkit_read.json +48 -0
- package/assets/tools/bantamkit_status.json +25 -0
- package/assets/tools/build_identity.json +17 -0
- package/assets/tools/document_list.json +12 -0
- package/assets/tools/document_read.json +31 -0
- package/assets/tools/file_graph.json +12 -0
- package/assets/tools/memory_compact.json +31 -0
- package/assets/tools/memory_recall.json +38 -0
- package/assets/tools/memory_save.json +61 -0
- package/assets/tools/shiftwork_clock_in.json +25 -0
- package/assets/tools/shiftwork_clock_out.json +60 -0
- package/assets/tools/shiftwork_status.json +25 -0
- package/assets/tools/skill_audit.json +70 -0
- package/assets/tools/validate_json.json +31 -0
- package/dist/assets.d.ts +48 -0
- package/dist/assets.js +109 -0
- package/dist/charsets.d.ts +17 -0
- package/dist/charsets.js +580 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +280 -0
- package/dist/contract.d.ts +145 -0
- package/dist/contract.js +608 -0
- package/dist/docread.d.ts +357 -0
- package/dist/docread.js +3315 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +14 -0
- package/dist/eventlog.d.ts +137 -0
- package/dist/eventlog.js +294 -0
- package/dist/htmlentities.d.ts +2 -0
- package/dist/htmlentities.js +16 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +13 -0
- package/dist/mcp/identity.d.ts +25 -0
- package/dist/mcp/identity.js +275 -0
- package/dist/mcp/pyargs.d.ts +80 -0
- package/dist/mcp/pyargs.js +362 -0
- package/dist/mcp/sdkjson.d.ts +56 -0
- package/dist/mcp/sdkjson.js +91 -0
- package/dist/mcp/server.d.ts +45 -0
- package/dist/mcp/server.js +740 -0
- package/dist/mcp/status.d.ts +210 -0
- package/dist/mcp/status.js +357 -0
- package/dist/mcp/transport.d.ts +68 -0
- package/dist/mcp/transport.js +111 -0
- package/dist/mcpreport.d.ts +214 -0
- package/dist/mcpreport.js +758 -0
- package/dist/memory/cli.d.ts +2 -0
- package/dist/memory/cli.js +396 -0
- package/dist/memory/component.d.ts +256 -0
- package/dist/memory/component.js +497 -0
- package/dist/memory/factfile.d.ts +120 -0
- package/dist/memory/factfile.js +406 -0
- package/dist/memory/layers.d.ts +92 -0
- package/dist/memory/layers.js +410 -0
- package/dist/memory/pyfs.d.ts +521 -0
- package/dist/memory/pyfs.js +1608 -0
- package/dist/memory/pyyaml.d.ts +155 -0
- package/dist/memory/pyyaml.js +868 -0
- package/dist/memory/store.d.ts +478 -0
- package/dist/memory/store.js +1034 -0
- package/dist/pyargparse.d.ts +141 -0
- package/dist/pyargparse.js +813 -0
- package/dist/pyjson.d.ts +110 -0
- package/dist/pyjson.js +524 -0
- package/dist/pyjsonschema.d.ts +50 -0
- package/dist/pyjsonschema.js +951 -0
- package/dist/shiftwork.d.ts +60 -0
- package/dist/shiftwork.js +324 -0
- package/dist/skillaudit.d.ts +172 -0
- package/dist/skillaudit.js +914 -0
- package/dist/statusline.d.ts +106 -0
- package/dist/statusline.js +281 -0
- package/package.json +45 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { PY_LINESEP } from '../memory/pyfs.js';
|
|
3
|
+
/** A JSON-RPC id is a string or a number; the map key is its JSON text, which is total. */
|
|
4
|
+
const idKey = (id) => JSON.stringify(id ?? null);
|
|
5
|
+
export class RawStdioTransport {
|
|
6
|
+
input;
|
|
7
|
+
output;
|
|
8
|
+
onclose;
|
|
9
|
+
onerror;
|
|
10
|
+
onmessage;
|
|
11
|
+
sessionId;
|
|
12
|
+
buffer = Buffer.alloc(0);
|
|
13
|
+
started = false;
|
|
14
|
+
rawLines = new Map();
|
|
15
|
+
exactResults = new Map();
|
|
16
|
+
ondata = (chunk) => this.consume(chunk);
|
|
17
|
+
onstreamerror = (error) => this.onerror?.(error);
|
|
18
|
+
constructor(input = process.stdin, output = process.stdout) {
|
|
19
|
+
this.input = input;
|
|
20
|
+
this.output = output;
|
|
21
|
+
}
|
|
22
|
+
/** The verbatim request line that carried this id, or `undefined` if it is gone. */
|
|
23
|
+
rawLineFor(id) {
|
|
24
|
+
return this.rawLines.get(idKey(id));
|
|
25
|
+
}
|
|
26
|
+
/** Register the exact JSON text to send as this request's `result`. */
|
|
27
|
+
setExactResult(id, json) {
|
|
28
|
+
this.exactResults.set(idKey(id), json);
|
|
29
|
+
}
|
|
30
|
+
async start() {
|
|
31
|
+
if (this.started)
|
|
32
|
+
throw new Error('RawStdioTransport already started');
|
|
33
|
+
this.started = true;
|
|
34
|
+
this.input.on('data', this.ondata);
|
|
35
|
+
this.input.on('error', this.onstreamerror);
|
|
36
|
+
}
|
|
37
|
+
async close() {
|
|
38
|
+
this.input.off('data', this.ondata);
|
|
39
|
+
this.input.off('error', this.onstreamerror);
|
|
40
|
+
if (this.input.listenerCount('data') === 0)
|
|
41
|
+
this.input.pause();
|
|
42
|
+
this.buffer = Buffer.alloc(0);
|
|
43
|
+
this.rawLines.clear();
|
|
44
|
+
this.exactResults.clear();
|
|
45
|
+
this.onclose?.();
|
|
46
|
+
}
|
|
47
|
+
send(message) {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
const text = this.frame(message);
|
|
50
|
+
// `os.linesep`, because the reference's stdout is a TEXT stream. The Python SDK writes
|
|
51
|
+
// its frames through `TextIOWrapper(sys.stdout.buffer)` with the default
|
|
52
|
+
// `newline=None`, so on Windows every frame CPython emits ends `\r\n`. MEASURED, run
|
|
53
|
+
// 32646521489: all nine `wire/*: raw frame bytes` cases differed by exactly one byte
|
|
54
|
+
// per frame and by nothing else. A client is free to strip it; a port that claims to
|
|
55
|
+
// be byte-compatible on the wire does not get to decide it is decoration.
|
|
56
|
+
if (this.output.write(`${text}${PY_LINESEP}`))
|
|
57
|
+
resolve();
|
|
58
|
+
else
|
|
59
|
+
this.output.once('drain', () => resolve());
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* One frame, in the reference's key order, with an exact `result` when one was registered.
|
|
64
|
+
*
|
|
65
|
+
* The raw line and the exact text are both dropped here rather than in the handler: the
|
|
66
|
+
* handler cannot know whether the protocol will actually send its answer (a cancelled
|
|
67
|
+
* request never reaches `send`), and a map that only ever grows is a leak in a process
|
|
68
|
+
* that is meant to run for a session.
|
|
69
|
+
*/
|
|
70
|
+
frame(message) {
|
|
71
|
+
const framed = message;
|
|
72
|
+
if (framed.id === undefined)
|
|
73
|
+
return JSON.stringify(message);
|
|
74
|
+
const key = idKey(framed.id);
|
|
75
|
+
this.rawLines.delete(key);
|
|
76
|
+
const id = JSON.stringify(framed.id);
|
|
77
|
+
if ('result' in framed) {
|
|
78
|
+
const exact = this.exactResults.get(key);
|
|
79
|
+
this.exactResults.delete(key);
|
|
80
|
+
return `{"jsonrpc":"2.0","id":${id},"result":${exact ?? JSON.stringify(framed.result)}}`;
|
|
81
|
+
}
|
|
82
|
+
this.exactResults.delete(key);
|
|
83
|
+
if ('error' in framed)
|
|
84
|
+
return `{"jsonrpc":"2.0","id":${id},"error":${JSON.stringify(framed.error)}}`;
|
|
85
|
+
return JSON.stringify(message);
|
|
86
|
+
}
|
|
87
|
+
consume(chunk) {
|
|
88
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
89
|
+
for (;;) {
|
|
90
|
+
const at = this.buffer.indexOf(0x0a);
|
|
91
|
+
if (at === -1)
|
|
92
|
+
return;
|
|
93
|
+
const line = this.buffer.toString('utf8', 0, at).replace(/\r$/, '');
|
|
94
|
+
this.buffer = this.buffer.subarray(at + 1);
|
|
95
|
+
if (line.trim() === '')
|
|
96
|
+
continue;
|
|
97
|
+
let message;
|
|
98
|
+
try {
|
|
99
|
+
message = JSONRPCMessageSchema.parse(JSON.parse(line));
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
this.onerror?.(error);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const id = message.id;
|
|
106
|
+
if (id !== undefined)
|
|
107
|
+
this.rawLines.set(idKey(id), line);
|
|
108
|
+
this.onmessage?.(message);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { formatTimestamp } from './eventlog.js';
|
|
2
|
+
/**
|
|
3
|
+
* Overrides the platform default for the host-log root. The default is only KNOWN on macOS
|
|
4
|
+
* (see `defaultHostLogRoot`), so this is what makes the analyst usable at all on Linux and
|
|
5
|
+
* Windows — and what lets a test run against a fixture tree.
|
|
6
|
+
*/
|
|
7
|
+
export declare const HOST_LOG_ROOT_ENV = "BANTAMKIT_HOST_LOG_ROOT";
|
|
8
|
+
/**
|
|
9
|
+
* The ONLY directory name read under a slug directory. `mcp-logs-clickup` and sixteen
|
|
10
|
+
* others sit beside it; globbing `mcp-logs-*` would read another program's log.
|
|
11
|
+
*/
|
|
12
|
+
export declare const HOST_LOG_DIRNAME = "mcp-logs-bantamkit";
|
|
13
|
+
/**
|
|
14
|
+
* How far outside the host's `[dispatch, completion]` interval an event's `ts` may fall and
|
|
15
|
+
* still be a CANDIDATE. Not a tolerance for clock skew — both stamps come from one machine
|
|
16
|
+
* clock — but for the lag between the component deciding and the host stamping the reply it
|
|
17
|
+
* eventually read. Widening it produces MORE ambiguity, not more matches.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_WINDOW_MS = 250;
|
|
20
|
+
/** Report format version. Bump when a section, a field or an order changes; both runtimes move together. */
|
|
21
|
+
export declare const REPORT_VERSION = 1;
|
|
22
|
+
/**
|
|
23
|
+
* Outcomes worth asking "and then what?" about. An `escalate` that ended a session and an
|
|
24
|
+
* `escalate` followed by nine more calls are different facts about the same word.
|
|
25
|
+
*/
|
|
26
|
+
export declare const FOLLOW_UP_OUTCOMES: readonly string[];
|
|
27
|
+
/**
|
|
28
|
+
* `2026-08-23T17:38:47.243Z` -> integer epoch milliseconds, or `null`.
|
|
29
|
+
*
|
|
30
|
+
* Hand-rolled rather than `Date.parse` for the reason the reference is hand-rolled rather
|
|
31
|
+
* than `datetime.fromisoformat`: each stdlib accepts a DIFFERENT wider set (`+00:00`, no
|
|
32
|
+
* `Z`, six fractional digits, `2026-08-23` alone), and this is a compared field. A parser
|
|
33
|
+
* that accepted more would put records on the timeline the host never put there. One regex
|
|
34
|
+
* that admits exactly the host's spelling is the same function on both sides.
|
|
35
|
+
*
|
|
36
|
+
* The arithmetic is done in integers here: no `Date` object is constructed on the way IN,
|
|
37
|
+
* so no local time zone can reach the answer. `formatTimestamp` is the event log's, which
|
|
38
|
+
* is `toISOString()` and is UTC by definition — the two are inverses over the range that
|
|
39
|
+
* matters.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseTimestamp(text: string): number | null;
|
|
42
|
+
/** The inverse, reusing the event log's formatter so one spelling exists in the repo. */
|
|
43
|
+
export { formatTimestamp };
|
|
44
|
+
/**
|
|
45
|
+
* One classified host line. THERE IS NO FIELD HERE THAT CAN HOLD HOST TEXT.
|
|
46
|
+
*
|
|
47
|
+
* `kind` is a token from this module's vocabulary, `tool` passed `[a-z0-9_]{1,64}`,
|
|
48
|
+
* `session` passed `[A-Za-z0-9-]{1,64}`, and the rest are integers. That is the whole leak
|
|
49
|
+
* guard: not a filter applied on the way out, but an absence of anywhere to put it.
|
|
50
|
+
*/
|
|
51
|
+
export interface HostRecord {
|
|
52
|
+
readonly tsMs: number;
|
|
53
|
+
readonly session: string;
|
|
54
|
+
readonly kind: 'call-start' | 'call-ok' | 'call-fail' | 'error' | 'other';
|
|
55
|
+
readonly tool: string | null;
|
|
56
|
+
readonly durationMs: number | null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One JSONL line -> a `HostRecord`, or `null` for anything unparseable.
|
|
60
|
+
*
|
|
61
|
+
* `null` covers a blank line, a truncated tail (the host is appending while this reads), a
|
|
62
|
+
* record with no usable timestamp, and a record with no `debug` and no `error` string. All
|
|
63
|
+
* of them are counted as `unclassified` by the caller and nothing else.
|
|
64
|
+
*/
|
|
65
|
+
export declare function parseHostLine(line: string): HostRecord | null;
|
|
66
|
+
/** A dispatch paired with its completion. Either end may be missing; say which. */
|
|
67
|
+
export interface HostCall {
|
|
68
|
+
readonly tool: string;
|
|
69
|
+
readonly session: string;
|
|
70
|
+
readonly startMs: number | null;
|
|
71
|
+
readonly endMs: number | null;
|
|
72
|
+
readonly ok: boolean | null;
|
|
73
|
+
readonly durationMs: number | null;
|
|
74
|
+
}
|
|
75
|
+
/** The one timestamp the join hangs on when only one end was logged. */
|
|
76
|
+
export declare function anchorMs(call: HostCall): number;
|
|
77
|
+
export interface PairedCalls {
|
|
78
|
+
readonly calls: HostCall[];
|
|
79
|
+
readonly incomplete: number;
|
|
80
|
+
readonly orphanCompletions: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Dispatches to completions, FIFO within one `(session, tool)`.
|
|
84
|
+
*
|
|
85
|
+
* FIFO and not nearest-time, because within one session and one tool the host's own file
|
|
86
|
+
* order IS the dispatch order — it is one writer appending. Across sessions or across tools
|
|
87
|
+
* no order is assumed, which is why the queue key carries both.
|
|
88
|
+
*
|
|
89
|
+
* Returns the calls plus two honesty counters: dispatches that never completed (the process
|
|
90
|
+
* was killed, or the file was rotated mid-call) and completions with no dispatch in view
|
|
91
|
+
* (the dispatch is in an earlier file that was rotated away).
|
|
92
|
+
*/
|
|
93
|
+
export declare function pairCalls(records: readonly HostRecord[]): PairedCalls;
|
|
94
|
+
/** What source A yielded, including the reason it yielded nothing. */
|
|
95
|
+
export interface HostScan {
|
|
96
|
+
readonly root: string | null;
|
|
97
|
+
readonly missing: null | 'not-found' | 'unknown-platform';
|
|
98
|
+
readonly platform: string;
|
|
99
|
+
readonly dirs: number;
|
|
100
|
+
readonly files: number;
|
|
101
|
+
readonly records: number;
|
|
102
|
+
readonly lifecycle: number;
|
|
103
|
+
readonly unclassified: number;
|
|
104
|
+
readonly errors: number;
|
|
105
|
+
readonly errorsWithTool: number;
|
|
106
|
+
readonly calls: HostCall[];
|
|
107
|
+
readonly incomplete: number;
|
|
108
|
+
readonly orphanCompletions: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* `~/Library/Caches/claude-cli-nodejs` — ON macOS, AND NOWHERE ELSE.
|
|
112
|
+
*
|
|
113
|
+
* Where Claude Code writes this on Linux and on Windows has NOT been measured, and a guess
|
|
114
|
+
* dressed as a default would produce an empty report that reads like "nothing happened". So
|
|
115
|
+
* off darwin this returns `null`, the report says which platform it is on and names
|
|
116
|
+
* `BANTAMKIT_HOST_LOG_ROOT`, and source B is reported alone. `CLAUDE.md`'s parity rule is
|
|
117
|
+
* satisfied by the analyst RUNNING on Windows and saying what it cannot see, not by
|
|
118
|
+
* pretending it looked. Lifting the limitation is a MEASUREMENT, not a code change.
|
|
119
|
+
*/
|
|
120
|
+
export declare function defaultHostLogRoot(platform: string, home: string): string | null;
|
|
121
|
+
export declare function resolveHostLogRoot(env: Readonly<Record<string, string | undefined>>, platform?: string, home?: string): string | null;
|
|
122
|
+
/**
|
|
123
|
+
* Every `<root>/*/mcp-logs-bantamkit/*.jsonl`, sorted, and NOTHING else.
|
|
124
|
+
*
|
|
125
|
+
* The slug directories are listed and each is asked for `mcp-logs-bantamkit` BY NAME. No
|
|
126
|
+
* `mcp-logs-*` pattern is ever built, so a sibling server's directory cannot be reached by
|
|
127
|
+
* this function even by accident — `mcp-logs-clickup` is right there.
|
|
128
|
+
*/
|
|
129
|
+
export declare function hostLogFiles(root: string): string[];
|
|
130
|
+
/**
|
|
131
|
+
* `platform` is carried into the scan, not read at render time, so an injected platform
|
|
132
|
+
* reaches the "unknown on this platform" line and a test can see it.
|
|
133
|
+
*/
|
|
134
|
+
export declare function scanHostLog(root: string | null, platform?: string): HostScan;
|
|
135
|
+
export interface Event {
|
|
136
|
+
readonly tsMs: number;
|
|
137
|
+
readonly tool: string;
|
|
138
|
+
readonly outcome: string;
|
|
139
|
+
}
|
|
140
|
+
export interface EventScan {
|
|
141
|
+
readonly path: string | null;
|
|
142
|
+
readonly missing: null | 'not-found' | 'off';
|
|
143
|
+
readonly records: number;
|
|
144
|
+
readonly unreadable: number;
|
|
145
|
+
readonly events: Event[];
|
|
146
|
+
}
|
|
147
|
+
/** One `docs/eventlog.md` record. Wrong shape or wrong `ts` -> `null`. */
|
|
148
|
+
export declare function parseEventLine(line: string): Event | null;
|
|
149
|
+
/**
|
|
150
|
+
* Reads the live file AND the one rotated generation, because the rotation rule keeps
|
|
151
|
+
* `<path>.1` and a report that ignored it would lose the older half of a session.
|
|
152
|
+
*/
|
|
153
|
+
export declare function scanEventLog(path: string | null): EventScan;
|
|
154
|
+
/**
|
|
155
|
+
* The whole result of the join, uncertainty first.
|
|
156
|
+
*
|
|
157
|
+
* `matched` holds only MUTUALLY UNIQUE pairs. `ambiguousGroups` holds the size of every
|
|
158
|
+
* component that was not one-to-one, as `[events, calls]`. Nothing is dropped: every event
|
|
159
|
+
* lands in exactly one of matched / ambiguous / unmatched, and so does every call.
|
|
160
|
+
*/
|
|
161
|
+
export interface Join {
|
|
162
|
+
readonly windowMs: number;
|
|
163
|
+
readonly matched: [Event, HostCall][];
|
|
164
|
+
readonly ambiguousGroups: [number, number][];
|
|
165
|
+
readonly ambiguousEvents: number;
|
|
166
|
+
readonly ambiguousCalls: number;
|
|
167
|
+
readonly unmatchedEvents: Event[];
|
|
168
|
+
readonly unmatchedCalls: HostCall[];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Mutual uniqueness or nothing.
|
|
172
|
+
*
|
|
173
|
+
* A greedy nearest-time pass would produce a bigger `matched` number and a report that lies
|
|
174
|
+
* about which session an outcome belongs to. The component rule cannot: an event whose
|
|
175
|
+
* candidate call is also some other event's candidate is not paired with it, and both land
|
|
176
|
+
* in one ambiguous group whose sizes are printed. The traversal is ITERATIVE — a busy day
|
|
177
|
+
* is thousands of calls and a recursive flood fill would blow the stack on the one input
|
|
178
|
+
* that matters.
|
|
179
|
+
*/
|
|
180
|
+
export declare function join(events: readonly Event[], calls: readonly HostCall[], windowMs: number): Join;
|
|
181
|
+
/**
|
|
182
|
+
* The LOWER median: an element of the input, never a mean.
|
|
183
|
+
*
|
|
184
|
+
* A mean of two integers is a float, a float has to be formatted, and two runtimes format
|
|
185
|
+
* floats differently at the edges. `Array.prototype.sort` is LEXICOGRAPHIC by default, so
|
|
186
|
+
* the numeric comparator is not decoration: `[9, 51]` sorts to `[51, 9]` without it.
|
|
187
|
+
*/
|
|
188
|
+
export declare function p50(values: readonly number[]): number;
|
|
189
|
+
/** The report, as the bytes `docs/mcpreport.md` specifies. No clock is read here. */
|
|
190
|
+
export declare function render(host: HostScan, events: EventScan, joined: Join): string;
|
|
191
|
+
export interface ReportOptions {
|
|
192
|
+
readonly windowMs?: number;
|
|
193
|
+
readonly platform?: string;
|
|
194
|
+
readonly home?: string;
|
|
195
|
+
}
|
|
196
|
+
export declare function buildReport(env: Readonly<Record<string, string | undefined>>, eventLogPath: string | null, options?: ReportOptions): string;
|
|
197
|
+
/**
|
|
198
|
+
* Where source B would be for THIS invocation, resolved exactly as the server resolves it.
|
|
199
|
+
*
|
|
200
|
+
* ONE VOCABULARY FOR `BANTAMKIT_EVENT_LOG`, NOT TWO: `eventlog.resolvePath` decides
|
|
201
|
+
* off / default / literal, and this only supplies the store root it needs. The root comes
|
|
202
|
+
* from `--store` when given and otherwise from `discoverProjectStore`, which walks up and
|
|
203
|
+
* DESIGNATES without creating anything — so asking for a report never brings a store into
|
|
204
|
+
* existence.
|
|
205
|
+
*
|
|
206
|
+
* A pinned `BANTAMKIT_MEMORY_DIR` that does not exist makes the walk throw. That is a real
|
|
207
|
+
* answer, not a crash to hide: the report then names the path the walk would have
|
|
208
|
+
* designated, and the `not found at` line says the rest.
|
|
209
|
+
*
|
|
210
|
+
* `pyJoin` on the way out because the reference returns a `Path` and prints `str(Path)`,
|
|
211
|
+
* which collapses `//` and `./` in a literal the operator typed. The file identity does not
|
|
212
|
+
* move; the printed line does, and the line is compared byte for byte.
|
|
213
|
+
*/
|
|
214
|
+
export declare function resolveEventLogPath(env: Readonly<Record<string, string | undefined>>, store?: string | null, start?: string | null): string | null;
|