@munin-cli/core 0.0.0-stage → 0.1.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/dist/analyze/build.d.ts +13 -0
- package/dist/analyze/build.d.ts.map +1 -0
- package/dist/analyze/build.js +223 -0
- package/dist/analyze/build.js.map +1 -0
- package/dist/analyze/cache.d.ts +18 -0
- package/dist/analyze/cache.d.ts.map +1 -0
- package/dist/analyze/cache.js +198 -0
- package/dist/analyze/cache.js.map +1 -0
- package/dist/analyze/day.d.ts +11 -0
- package/dist/analyze/day.d.ts.map +1 -0
- package/dist/analyze/day.js +33 -0
- package/dist/analyze/day.js.map +1 -0
- package/dist/analyze/findings/index.d.ts +3 -0
- package/dist/analyze/findings/index.d.ts.map +1 -0
- package/dist/analyze/findings/index.js +11 -0
- package/dist/analyze/findings/index.js.map +1 -0
- package/dist/analyze/findings/repeat-tool-calls.d.ts +16 -0
- package/dist/analyze/findings/repeat-tool-calls.d.ts.map +1 -0
- package/dist/analyze/findings/repeat-tool-calls.js +168 -0
- package/dist/analyze/findings/repeat-tool-calls.js.map +1 -0
- package/dist/analyze/findings/repeated-context.d.ts +22 -0
- package/dist/analyze/findings/repeated-context.d.ts.map +1 -0
- package/dist/analyze/findings/repeated-context.js +113 -0
- package/dist/analyze/findings/repeated-context.js.map +1 -0
- package/dist/analyze/findings.d.ts +64 -0
- package/dist/analyze/findings.d.ts.map +1 -0
- package/dist/analyze/findings.js +288 -0
- package/dist/analyze/findings.js.map +1 -0
- package/dist/analyze/index.d.ts +18 -0
- package/dist/analyze/index.d.ts.map +1 -0
- package/dist/analyze/index.js +18 -0
- package/dist/analyze/index.js.map +1 -0
- package/dist/analyze/projection.d.ts +21 -0
- package/dist/analyze/projection.d.ts.map +1 -0
- package/dist/analyze/projection.js +33 -0
- package/dist/analyze/projection.js.map +1 -0
- package/dist/analyze/recommend.d.ts +12 -0
- package/dist/analyze/recommend.d.ts.map +1 -0
- package/dist/analyze/recommend.js +78 -0
- package/dist/analyze/recommend.js.map +1 -0
- package/dist/analyze/results.d.ts +70 -0
- package/dist/analyze/results.d.ts.map +1 -0
- package/dist/analyze/results.js +189 -0
- package/dist/analyze/results.js.map +1 -0
- package/dist/analyze/rules/apply.d.ts +113 -0
- package/dist/analyze/rules/apply.d.ts.map +1 -0
- package/dist/analyze/rules/apply.js +251 -0
- package/dist/analyze/rules/apply.js.map +1 -0
- package/dist/analyze/rules/confidence.d.ts +20 -0
- package/dist/analyze/rules/confidence.d.ts.map +1 -0
- package/dist/analyze/rules/confidence.js +44 -0
- package/dist/analyze/rules/confidence.js.map +1 -0
- package/dist/analyze/rules/ported.d.ts +17 -0
- package/dist/analyze/rules/ported.d.ts.map +1 -0
- package/dist/analyze/rules/ported.js +701 -0
- package/dist/analyze/rules/ported.js.map +1 -0
- package/dist/analyze/rules/replay.d.ts +53 -0
- package/dist/analyze/rules/replay.d.ts.map +1 -0
- package/dist/analyze/rules/replay.js +179 -0
- package/dist/analyze/rules/replay.js.map +1 -0
- package/dist/analyze/rules.d.ts +32 -0
- package/dist/analyze/rules.d.ts.map +1 -0
- package/dist/analyze/rules.js +220 -0
- package/dist/analyze/rules.js.map +1 -0
- package/dist/analyze/sessions.d.ts +20 -0
- package/dist/analyze/sessions.d.ts.map +1 -0
- package/dist/analyze/sessions.js +131 -0
- package/dist/analyze/sessions.js.map +1 -0
- package/dist/analyze/snapshot-diff.d.ts +4 -0
- package/dist/analyze/snapshot-diff.d.ts.map +1 -0
- package/dist/analyze/snapshot-diff.js +63 -0
- package/dist/analyze/snapshot-diff.js.map +1 -0
- package/dist/analyze/summary.d.ts +25 -0
- package/dist/analyze/summary.d.ts.map +1 -0
- package/dist/analyze/summary.js +227 -0
- package/dist/analyze/summary.js.map +1 -0
- package/dist/analyze/turns.d.ts +23 -0
- package/dist/analyze/turns.d.ts.map +1 -0
- package/dist/analyze/turns.js +46 -0
- package/dist/analyze/turns.js.map +1 -0
- package/dist/analyze/verification.d.ts +29 -0
- package/dist/analyze/verification.d.ts.map +1 -0
- package/dist/analyze/verification.js +116 -0
- package/dist/analyze/verification.js.map +1 -0
- package/dist/diagnostics.d.ts +65 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +59 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/domain/analysis.d.ts +243 -0
- package/dist/domain/analysis.d.ts.map +1 -0
- package/dist/domain/analysis.js +2 -0
- package/dist/domain/analysis.js.map +1 -0
- package/dist/domain/cost.d.ts +17 -0
- package/dist/domain/cost.d.ts.map +1 -0
- package/dist/domain/cost.js +17 -0
- package/dist/domain/cost.js.map +1 -0
- package/dist/domain/finding.d.ts +216 -0
- package/dist/domain/finding.d.ts.map +1 -0
- package/dist/domain/finding.js +8 -0
- package/dist/domain/finding.js.map +1 -0
- package/dist/domain/index.d.ts +10 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +10 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/ledger.d.ts +87 -0
- package/dist/domain/ledger.d.ts.map +1 -0
- package/dist/domain/ledger.js +2 -0
- package/dist/domain/ledger.js.map +1 -0
- package/dist/domain/recommendation.d.ts +30 -0
- package/dist/domain/recommendation.d.ts.map +1 -0
- package/dist/domain/recommendation.js +2 -0
- package/dist/domain/recommendation.js.map +1 -0
- package/dist/domain/report.d.ts +193 -0
- package/dist/domain/report.d.ts.map +1 -0
- package/dist/domain/report.js +2 -0
- package/dist/domain/report.js.map +1 -0
- package/dist/domain/source.d.ts +96 -0
- package/dist/domain/source.d.ts.map +1 -0
- package/dist/domain/source.js +2 -0
- package/dist/domain/source.js.map +1 -0
- package/dist/domain/usage.d.ts +141 -0
- package/dist/domain/usage.d.ts.map +1 -0
- package/dist/domain/usage.js +9 -0
- package/dist/domain/usage.js.map +1 -0
- package/dist/domain/verification.d.ts +14 -0
- package/dist/domain/verification.d.ts.map +1 -0
- package/dist/domain/verification.js +2 -0
- package/dist/domain/verification.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger/cursor.d.ts +16 -0
- package/dist/ledger/cursor.d.ts.map +1 -0
- package/dist/ledger/cursor.js +43 -0
- package/dist/ledger/cursor.js.map +1 -0
- package/dist/ledger/schemas.d.ts +8 -0
- package/dist/ledger/schemas.d.ts.map +1 -0
- package/dist/ledger/schemas.js +179 -0
- package/dist/ledger/schemas.js.map +1 -0
- package/dist/ledger/slices.d.ts +9 -0
- package/dist/ledger/slices.d.ts.map +1 -0
- package/dist/ledger/slices.js +234 -0
- package/dist/ledger/slices.js.map +1 -0
- package/dist/normalize/merge.d.ts +11 -0
- package/dist/normalize/merge.d.ts.map +1 -0
- package/dist/normalize/merge.js +47 -0
- package/dist/normalize/merge.js.map +1 -0
- package/dist/normalize/opencode-v1.d.ts +10 -0
- package/dist/normalize/opencode-v1.d.ts.map +1 -0
- package/dist/normalize/opencode-v1.js +144 -0
- package/dist/normalize/opencode-v1.js.map +1 -0
- package/dist/normalize/opencode-v2.d.ts +20 -0
- package/dist/normalize/opencode-v2.d.ts.map +1 -0
- package/dist/normalize/opencode-v2.js +217 -0
- package/dist/normalize/opencode-v2.js.map +1 -0
- package/dist/normalize/reconcile.d.ts +20 -0
- package/dist/normalize/reconcile.d.ts.map +1 -0
- package/dist/normalize/reconcile.js +72 -0
- package/dist/normalize/reconcile.js.map +1 -0
- package/dist/normalize/rows.d.ts +81 -0
- package/dist/normalize/rows.d.ts.map +1 -0
- package/dist/normalize/rows.js +8 -0
- package/dist/normalize/rows.js.map +1 -0
- package/dist/normalize/text.d.ts +2 -0
- package/dist/normalize/text.d.ts.map +1 -0
- package/dist/normalize/text.js +13 -0
- package/dist/normalize/text.js.map +1 -0
- package/dist/normalize/time.d.ts +2 -0
- package/dist/normalize/time.d.ts.map +1 -0
- package/dist/normalize/time.js +7 -0
- package/dist/normalize/time.js.map +1 -0
- package/dist/normalize/tools.d.ts +4 -0
- package/dist/normalize/tools.d.ts.map +1 -0
- package/dist/normalize/tools.js +149 -0
- package/dist/normalize/tools.js.map +1 -0
- package/dist/pricing/catalogue/anthropic.d.ts +8 -0
- package/dist/pricing/catalogue/anthropic.d.ts.map +1 -0
- package/dist/pricing/catalogue/anthropic.js +169 -0
- package/dist/pricing/catalogue/anthropic.js.map +1 -0
- package/dist/pricing/catalogue/google.d.ts +9 -0
- package/dist/pricing/catalogue/google.d.ts.map +1 -0
- package/dist/pricing/catalogue/google.js +48 -0
- package/dist/pricing/catalogue/google.js.map +1 -0
- package/dist/pricing/catalogue/ollama.d.ts +9 -0
- package/dist/pricing/catalogue/ollama.d.ts.map +1 -0
- package/dist/pricing/catalogue/ollama.js +17 -0
- package/dist/pricing/catalogue/ollama.js.map +1 -0
- package/dist/pricing/catalogue/open.d.ts +9 -0
- package/dist/pricing/catalogue/open.d.ts.map +1 -0
- package/dist/pricing/catalogue/open.js +108 -0
- package/dist/pricing/catalogue/open.js.map +1 -0
- package/dist/pricing/catalogue/openai.d.ts +8 -0
- package/dist/pricing/catalogue/openai.d.ts.map +1 -0
- package/dist/pricing/catalogue/openai.js +291 -0
- package/dist/pricing/catalogue/openai.js.map +1 -0
- package/dist/pricing/catalogue/shared.d.ts +29 -0
- package/dist/pricing/catalogue/shared.d.ts.map +1 -0
- package/dist/pricing/catalogue/shared.js +86 -0
- package/dist/pricing/catalogue/shared.js.map +1 -0
- package/dist/pricing/cost.d.ts +42 -0
- package/dist/pricing/cost.d.ts.map +1 -0
- package/dist/pricing/cost.js +180 -0
- package/dist/pricing/cost.js.map +1 -0
- package/dist/pricing/index.d.ts +10 -0
- package/dist/pricing/index.d.ts.map +1 -0
- package/dist/pricing/index.js +11 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/pricing/providers.d.ts +19 -0
- package/dist/pricing/providers.d.ts.map +1 -0
- package/dist/pricing/providers.js +35 -0
- package/dist/pricing/providers.js.map +1 -0
- package/dist/pricing/resolve.d.ts +20 -0
- package/dist/pricing/resolve.d.ts.map +1 -0
- package/dist/pricing/resolve.js +79 -0
- package/dist/pricing/resolve.js.map +1 -0
- package/dist/pricing/types.d.ts +158 -0
- package/dist/pricing/types.d.ts.map +1 -0
- package/dist/pricing/types.js +2 -0
- package/dist/pricing/types.js.map +1 -0
- package/dist/report/report.d.ts +32 -0
- package/dist/report/report.d.ts.map +1 -0
- package/dist/report/report.js +269 -0
- package/dist/report/report.js.map +1 -0
- package/package.json +25 -4
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 semideus-dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PricedDataset, Window } from "../domain/analysis.js";
|
|
2
|
+
import type { Dataset } from "../domain/source.js";
|
|
3
|
+
import type { UsageEvent } from "../domain/usage.js";
|
|
4
|
+
/** Stable source ordering shared by batch and ledger report builds. */
|
|
5
|
+
export declare function sortUsageEvents(events: readonly UsageEvent[]): UsageEvent[];
|
|
6
|
+
/** First event time to the last end (or start) time; never less than one day. */
|
|
7
|
+
export declare function windowOf(events: UsageEvent[]): Window;
|
|
8
|
+
/**
|
|
9
|
+
* Turn a dataset into priced events, every attempt, a window, and warnings.
|
|
10
|
+
* The input dataset is never mutated.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildEvents(dataset: Dataset): PricedDataset;
|
|
13
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/analyze/build.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EACV,OAAO,EAKR,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAkIpE,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,EAAE,CAE3E;AAQD,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAiCrD;AA2BD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,CAoC3D"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build priced events from a Phase 2 dataset.
|
|
3
|
+
*
|
|
4
|
+
* This is the seam between what the source recorded and what reports read. It
|
|
5
|
+
* is pure: no filesystem, network, or clock access. Every price is selected
|
|
6
|
+
* from the call's own timestamp.
|
|
7
|
+
*/
|
|
8
|
+
import { WARNING_CODES } from "../diagnostics.js";
|
|
9
|
+
import { billableTokens, estimateCost } from "../pricing/cost.js";
|
|
10
|
+
import { resolvePrice } from "../pricing/resolve.js";
|
|
11
|
+
import { messageContexts } from "./turns.js";
|
|
12
|
+
function isModelCall(message) {
|
|
13
|
+
return message.type === "assistant" || message.type === "compaction";
|
|
14
|
+
}
|
|
15
|
+
function schemaFor(dataset, session) {
|
|
16
|
+
if (session !== undefined)
|
|
17
|
+
return session.schema;
|
|
18
|
+
if (dataset.schema === "v1" || dataset.schema === "v2")
|
|
19
|
+
return dataset.schema;
|
|
20
|
+
// A message without its session should not happen. When it does, keep the
|
|
21
|
+
// event usable rather than throwing; the missing session is already visible
|
|
22
|
+
// in data health.
|
|
23
|
+
return "v2";
|
|
24
|
+
}
|
|
25
|
+
function latencyMs(start, end) {
|
|
26
|
+
if (end === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
const startMs = Date.parse(start);
|
|
29
|
+
const endMs = Date.parse(end);
|
|
30
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs))
|
|
31
|
+
return undefined;
|
|
32
|
+
const delta = endMs - startMs;
|
|
33
|
+
return delta >= 0 ? delta : undefined;
|
|
34
|
+
}
|
|
35
|
+
function attemptFrom(message, session) {
|
|
36
|
+
const model = message.model ?? session?.model;
|
|
37
|
+
const agent = message.agent ?? session?.agent;
|
|
38
|
+
return {
|
|
39
|
+
id: message.id,
|
|
40
|
+
sessionId: message.sessionId,
|
|
41
|
+
ts: message.timeCreated,
|
|
42
|
+
type: message.type === "compaction" ? "compaction" : "assistant",
|
|
43
|
+
...(model === undefined ? {} : { model: { ...model } }),
|
|
44
|
+
...(agent === undefined ? {} : { agent }),
|
|
45
|
+
...(message.usage === undefined
|
|
46
|
+
? {}
|
|
47
|
+
: {
|
|
48
|
+
usage: {
|
|
49
|
+
tokens: { ...message.usage.tokens },
|
|
50
|
+
...(message.usage.observedUsd === undefined
|
|
51
|
+
? {}
|
|
52
|
+
: { observedUsd: message.usage.observedUsd }),
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
...(message.usage?.observedUsd === undefined ? {} : { observedUsd: message.usage.observedUsd }),
|
|
56
|
+
...(message.stopReason === undefined ? {} : { stopReason: message.stopReason }),
|
|
57
|
+
...(message.errorType === undefined ? {} : { errorType: message.errorType }),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function eventFrom(dataset, message, session, context) {
|
|
61
|
+
// The caller guarantees usage; the assertion keeps the map below total.
|
|
62
|
+
const usage = message.usage;
|
|
63
|
+
const billed = billableTokens(usage.tokens);
|
|
64
|
+
const model = message.model ?? session?.model;
|
|
65
|
+
const variant = message.model?.variant ?? session?.model?.variant;
|
|
66
|
+
const agent = message.agent ?? session?.agent;
|
|
67
|
+
const modelId = model?.model ?? "unknown";
|
|
68
|
+
const instructions = session?.instructions;
|
|
69
|
+
const endTs = message.completedAt;
|
|
70
|
+
const latency = latencyMs(message.timeCreated, endTs);
|
|
71
|
+
const turn = context ?? { contextEpoch: 0 };
|
|
72
|
+
return {
|
|
73
|
+
id: message.id,
|
|
74
|
+
source: "opencode",
|
|
75
|
+
schema: schemaFor(dataset, session),
|
|
76
|
+
provider: model?.provider ?? "unknown",
|
|
77
|
+
model: modelId,
|
|
78
|
+
...(variant === undefined ? {} : { variant }),
|
|
79
|
+
...(agent === undefined ? {} : { agent }),
|
|
80
|
+
ts: message.timeCreated,
|
|
81
|
+
sessionId: message.sessionId,
|
|
82
|
+
...(session?.parentId === undefined ? {} : { parentSessionId: session.parentId }),
|
|
83
|
+
...(session?.projectId === undefined ? {} : { projectId: session.projectId }),
|
|
84
|
+
project: session?.project ?? session?.projectId ?? "unknown",
|
|
85
|
+
...(turn.turnId === undefined ? {} : { turnId: turn.turnId }),
|
|
86
|
+
...(turn.turnStartedAt === undefined ? {} : { turnStartedAt: turn.turnStartedAt }),
|
|
87
|
+
...(endTs === undefined ? {} : { endTs }),
|
|
88
|
+
contextEpoch: turn.contextEpoch,
|
|
89
|
+
inputTokens: billed.input,
|
|
90
|
+
// Priced output includes reasoning, which the provider bills as output.
|
|
91
|
+
outputTokens: billed.output,
|
|
92
|
+
thinkingTokens: billed.thinking,
|
|
93
|
+
cacheReadTokens: billed.cacheRead,
|
|
94
|
+
cacheWrite5mTokens: billed.cacheWrite,
|
|
95
|
+
cacheWrite1hTokens: 0,
|
|
96
|
+
...(latency === undefined ? {} : { latencyMs: latency }),
|
|
97
|
+
...(message.stopReason === undefined ? {} : { stopReason: message.stopReason }),
|
|
98
|
+
isSubagent: session?.parentId !== undefined,
|
|
99
|
+
tools: (message.toolCalls ?? []).map((tool) => ({ ...tool })),
|
|
100
|
+
...(instructions === undefined
|
|
101
|
+
? {}
|
|
102
|
+
: {
|
|
103
|
+
instructionKeys: [...instructions.keys],
|
|
104
|
+
instructionChars: instructions.chars,
|
|
105
|
+
prefixHash: instructions.prefixHash,
|
|
106
|
+
}),
|
|
107
|
+
cost: estimateCost({
|
|
108
|
+
tokens: billed,
|
|
109
|
+
model: modelId,
|
|
110
|
+
...(usage.observedUsd === undefined ? {} : { observedUsd: usage.observedUsd }),
|
|
111
|
+
at: message.timeCreated,
|
|
112
|
+
}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function compareEvents(a, b) {
|
|
116
|
+
if (a.ts !== b.ts)
|
|
117
|
+
return a.ts.localeCompare(b.ts);
|
|
118
|
+
if (a.sessionId !== b.sessionId)
|
|
119
|
+
return a.sessionId.localeCompare(b.sessionId);
|
|
120
|
+
return a.id.localeCompare(b.id);
|
|
121
|
+
}
|
|
122
|
+
/** Stable source ordering shared by batch and ledger report builds. */
|
|
123
|
+
export function sortUsageEvents(events) {
|
|
124
|
+
return [...events].sort(compareEvents);
|
|
125
|
+
}
|
|
126
|
+
function compareAttempts(a, b) {
|
|
127
|
+
if (a.ts !== b.ts)
|
|
128
|
+
return a.ts.localeCompare(b.ts);
|
|
129
|
+
if (a.sessionId !== b.sessionId)
|
|
130
|
+
return a.sessionId.localeCompare(b.sessionId);
|
|
131
|
+
return a.id.localeCompare(b.id);
|
|
132
|
+
}
|
|
133
|
+
/** First event time to the last end (or start) time; never less than one day. */
|
|
134
|
+
export function windowOf(events) {
|
|
135
|
+
const first = events[0];
|
|
136
|
+
if (first === undefined)
|
|
137
|
+
return { start: null, end: null, days: 0 };
|
|
138
|
+
let startMs = Date.parse(first.ts);
|
|
139
|
+
let start = first.ts;
|
|
140
|
+
let end = first.endTs ?? first.ts;
|
|
141
|
+
let endMs = Date.parse(end);
|
|
142
|
+
for (const event of events) {
|
|
143
|
+
const startCandidateMs = Date.parse(event.ts);
|
|
144
|
+
if (Number.isFinite(startCandidateMs) && startCandidateMs < startMs) {
|
|
145
|
+
// Same rule as sorted input: the earliest start wins. buildEvents() output
|
|
146
|
+
// is sorted, so this matches its window exactly and survives unsorted input.
|
|
147
|
+
startMs = startCandidateMs;
|
|
148
|
+
start = event.ts;
|
|
149
|
+
}
|
|
150
|
+
const candidate = event.endTs ?? event.ts;
|
|
151
|
+
const candidateMs = Date.parse(candidate);
|
|
152
|
+
if (Number.isFinite(candidateMs) && (!Number.isFinite(endMs) || candidateMs > endMs)) {
|
|
153
|
+
end = candidate;
|
|
154
|
+
endMs = candidateMs;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// startMs is always the earliest finite start: initialised from the first
|
|
158
|
+
// event and only ever lowered by a valid timestamp.
|
|
159
|
+
const days = Number.isFinite(startMs) && Number.isFinite(endMs) && endMs >= startMs
|
|
160
|
+
? Math.max(1, (endMs - startMs) / 86_400_000)
|
|
161
|
+
: 1;
|
|
162
|
+
return { start, end, days };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* One `price.unpriced-model` per model with no card, aggregated with its call
|
|
166
|
+
* count and observed spend. An observed charge does not make a model priced: it
|
|
167
|
+
* makes that call's headline observed, but the model still cannot be estimated.
|
|
168
|
+
*/
|
|
169
|
+
function addPricingWarnings(events, warnings) {
|
|
170
|
+
const unpriced = new Map();
|
|
171
|
+
for (const event of events) {
|
|
172
|
+
if (resolvePrice(event.model) !== null)
|
|
173
|
+
continue;
|
|
174
|
+
const row = unpriced.get(event.model) ?? { calls: 0, observedUsd: 0 };
|
|
175
|
+
row.calls += 1;
|
|
176
|
+
row.observedUsd += event.cost.observedUsd ?? 0;
|
|
177
|
+
unpriced.set(event.model, row);
|
|
178
|
+
}
|
|
179
|
+
for (const [model, row] of [...unpriced.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
180
|
+
warnings.push({
|
|
181
|
+
code: WARNING_CODES.priceUnpricedModel,
|
|
182
|
+
message: `No price card for ${model}: ${row.calls} call(s), observed spend $${row.observedUsd}`,
|
|
183
|
+
count: row.calls,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Turn a dataset into priced events, every attempt, a window, and warnings.
|
|
189
|
+
* The input dataset is never mutated.
|
|
190
|
+
*/
|
|
191
|
+
export function buildEvents(dataset) {
|
|
192
|
+
const sessions = new Map(dataset.sessions.map((session) => [session.id, session]));
|
|
193
|
+
const contexts = messageContexts(dataset.messages);
|
|
194
|
+
const events = [];
|
|
195
|
+
const attempts = [];
|
|
196
|
+
for (const message of dataset.messages) {
|
|
197
|
+
if (!isModelCall(message))
|
|
198
|
+
continue;
|
|
199
|
+
const session = sessions.get(message.sessionId);
|
|
200
|
+
attempts.push(attemptFrom(message, session));
|
|
201
|
+
if (message.usage === undefined)
|
|
202
|
+
continue;
|
|
203
|
+
events.push(eventFrom(dataset, message, session, contexts.get(message.id)));
|
|
204
|
+
}
|
|
205
|
+
events.sort(compareEvents);
|
|
206
|
+
attempts.sort(compareAttempts);
|
|
207
|
+
const warnings = [...dataset.warnings];
|
|
208
|
+
addPricingWarnings(events, warnings);
|
|
209
|
+
if (events.length === 0) {
|
|
210
|
+
warnings.push({
|
|
211
|
+
code: WARNING_CODES.analysisNoEvents,
|
|
212
|
+
message: "No usage-bearing model calls were found",
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
schema: dataset.schema,
|
|
217
|
+
events,
|
|
218
|
+
attempts,
|
|
219
|
+
window: windowOf(events),
|
|
220
|
+
warnings,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/analyze/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache read/write analysis.
|
|
3
|
+
*
|
|
4
|
+
* Descriptive only: numbers here describe what happened, and nothing claims a
|
|
5
|
+
* low hit rate is waste (that judgement is a detector in Phase 4). Cache costs
|
|
6
|
+
* are always the list-price estimate; the observed total is never rebuilt from
|
|
7
|
+
* cache categories.
|
|
8
|
+
*/
|
|
9
|
+
import type { CacheStats, CacheStatsOptions } from "../domain/analysis.js";
|
|
10
|
+
import type { UsageEvent } from "../domain/usage.js";
|
|
11
|
+
/**
|
|
12
|
+
* Cache read/write behaviour over priced events.
|
|
13
|
+
*
|
|
14
|
+
* Events are consumed in the order given; buildEvents() output is already in a
|
|
15
|
+
* stable order.
|
|
16
|
+
*/
|
|
17
|
+
export declare function cacheStats(events: UsageEvent[], options?: CacheStatsOptions): CacheStats;
|
|
18
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/analyze/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAEV,UAAU,EACV,iBAAiB,EAIlB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAqGrD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,OAAO,GAAE,iBAAsB,GAAG,UAAU,CAqC5F"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { dayKey } from "./day.js";
|
|
2
|
+
/** Codepoint order, so results never depend on the runtime locale. */
|
|
3
|
+
function asc(a, b) {
|
|
4
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
5
|
+
}
|
|
6
|
+
function emptyAcc() {
|
|
7
|
+
return {
|
|
8
|
+
calls: 0,
|
|
9
|
+
inputTokens: 0,
|
|
10
|
+
readTokens: 0,
|
|
11
|
+
writeTokens: 0,
|
|
12
|
+
write5mTokens: 0,
|
|
13
|
+
write1hTokens: 0,
|
|
14
|
+
estimatedReadUsd: 0,
|
|
15
|
+
estimatedWriteUsd: 0,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function addEvent(acc, event) {
|
|
19
|
+
acc.calls += 1;
|
|
20
|
+
acc.inputTokens += event.inputTokens;
|
|
21
|
+
acc.readTokens += event.cacheReadTokens;
|
|
22
|
+
acc.write5mTokens += event.cacheWrite5mTokens;
|
|
23
|
+
acc.write1hTokens += event.cacheWrite1hTokens;
|
|
24
|
+
acc.writeTokens += event.cacheWrite5mTokens + event.cacheWrite1hTokens;
|
|
25
|
+
acc.estimatedReadUsd += event.cost.cacheRead;
|
|
26
|
+
acc.estimatedWriteUsd += event.cost.cacheWrite;
|
|
27
|
+
}
|
|
28
|
+
function hitRate(acc) {
|
|
29
|
+
const inputSide = acc.inputTokens + acc.readTokens + acc.writeTokens;
|
|
30
|
+
return inputSide === 0 ? null : acc.readTokens / inputSide;
|
|
31
|
+
}
|
|
32
|
+
function accumulate(events) {
|
|
33
|
+
const acc = emptyAcc();
|
|
34
|
+
for (const event of events)
|
|
35
|
+
addEvent(acc, event);
|
|
36
|
+
return acc;
|
|
37
|
+
}
|
|
38
|
+
function toBreakdown(key, events) {
|
|
39
|
+
const acc = accumulate(events);
|
|
40
|
+
// Change over the window: the later half of the calls against the earlier
|
|
41
|
+
// half, in event order. With an odd count the middle call is in neither.
|
|
42
|
+
const mid = Math.floor(events.length / 2);
|
|
43
|
+
let hitRateChange = null;
|
|
44
|
+
if (mid > 0) {
|
|
45
|
+
const earlier = accumulate(events.slice(0, mid));
|
|
46
|
+
const later = accumulate(events.slice(events.length - mid));
|
|
47
|
+
const earlierRate = hitRate(earlier);
|
|
48
|
+
const laterRate = hitRate(later);
|
|
49
|
+
if (earlierRate !== null && laterRate !== null)
|
|
50
|
+
hitRateChange = laterRate - earlierRate;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
key,
|
|
54
|
+
calls: acc.calls,
|
|
55
|
+
inputTokens: acc.inputTokens,
|
|
56
|
+
readTokens: acc.readTokens,
|
|
57
|
+
writeTokens: acc.writeTokens,
|
|
58
|
+
write5mTokens: acc.write5mTokens,
|
|
59
|
+
write1hTokens: acc.write1hTokens,
|
|
60
|
+
hitRate: hitRate(acc),
|
|
61
|
+
estimatedReadUsd: acc.estimatedReadUsd,
|
|
62
|
+
estimatedWriteUsd: acc.estimatedWriteUsd,
|
|
63
|
+
hitRateChange,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function byCacheSpendDescThenKey(a, b) {
|
|
67
|
+
const aCache = a.estimatedReadUsd + a.estimatedWriteUsd;
|
|
68
|
+
const bCache = b.estimatedReadUsd + b.estimatedWriteUsd;
|
|
69
|
+
if (aCache !== bCache)
|
|
70
|
+
return bCache - aCache;
|
|
71
|
+
return asc(a.key, b.key);
|
|
72
|
+
}
|
|
73
|
+
function breakdown(map) {
|
|
74
|
+
const rows = [];
|
|
75
|
+
for (const [key, events] of map)
|
|
76
|
+
rows.push(toBreakdown(key, events));
|
|
77
|
+
return rows.sort(byCacheSpendDescThenKey);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Cache read/write behaviour over priced events.
|
|
81
|
+
*
|
|
82
|
+
* Events are consumed in the order given; buildEvents() output is already in a
|
|
83
|
+
* stable order.
|
|
84
|
+
*/
|
|
85
|
+
export function cacheStats(events, options = {}) {
|
|
86
|
+
const zone = options.zone ?? "UTC";
|
|
87
|
+
const totals = emptyAcc();
|
|
88
|
+
const byModel = new Map();
|
|
89
|
+
const byProvider = new Map();
|
|
90
|
+
const bySession = new Map();
|
|
91
|
+
const byDay = new Map();
|
|
92
|
+
for (const event of events) {
|
|
93
|
+
addEvent(totals, event);
|
|
94
|
+
pushTo(byModel, `${event.provider}/${event.model}`, event);
|
|
95
|
+
pushTo(byProvider, event.provider, event);
|
|
96
|
+
pushTo(bySession, event.sessionId, event);
|
|
97
|
+
pushTo(byDay, dayKey(event.ts, zone), event);
|
|
98
|
+
}
|
|
99
|
+
const estimatedTotal = events.reduce((sum, event) => sum + (event.cost.estimatedUsd ?? 0), 0);
|
|
100
|
+
const cacheUsd = totals.estimatedReadUsd + totals.estimatedWriteUsd;
|
|
101
|
+
return {
|
|
102
|
+
inputTokens: totals.inputTokens,
|
|
103
|
+
readTokens: totals.readTokens,
|
|
104
|
+
writeTokens: totals.writeTokens,
|
|
105
|
+
write5mTokens: totals.write5mTokens,
|
|
106
|
+
write1hTokens: totals.write1hTokens,
|
|
107
|
+
hitRate: hitRate(totals),
|
|
108
|
+
estimatedReadUsd: totals.estimatedReadUsd,
|
|
109
|
+
estimatedWriteUsd: totals.estimatedWriteUsd,
|
|
110
|
+
shareOfEstimate: estimatedTotal === 0 ? 0 : cacheUsd / estimatedTotal,
|
|
111
|
+
writesRecorded: totals.writeTokens > 0,
|
|
112
|
+
byModel: breakdown(byModel),
|
|
113
|
+
byProvider: breakdown(byProvider),
|
|
114
|
+
bySession: breakdown(bySession),
|
|
115
|
+
byDay: breakdown(byDay),
|
|
116
|
+
instructionContext: instructionContextOf(events),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function pushTo(map, key, event) {
|
|
120
|
+
const list = map.get(key);
|
|
121
|
+
if (list === undefined)
|
|
122
|
+
map.set(key, [event]);
|
|
123
|
+
else
|
|
124
|
+
list.push(event);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Instruction context, grouped by the cache identity
|
|
128
|
+
* `prefixHash + agent + model + variant`, never the prefix hash alone: two
|
|
129
|
+
* agents on one model share nothing at request time.
|
|
130
|
+
*/
|
|
131
|
+
function instructionContextOf(events) {
|
|
132
|
+
const seenSessions = new Set();
|
|
133
|
+
const prefixHashes = new Set();
|
|
134
|
+
let totalChars = 0;
|
|
135
|
+
const groups = new Map();
|
|
136
|
+
for (const event of events) {
|
|
137
|
+
const hasInstructions = event.instructionChars !== undefined || event.prefixHash !== undefined;
|
|
138
|
+
if (hasInstructions && !seenSessions.has(event.sessionId)) {
|
|
139
|
+
// Instruction size and prefix are session properties; count them on the
|
|
140
|
+
// first observed event of the session, never per call.
|
|
141
|
+
seenSessions.add(event.sessionId);
|
|
142
|
+
if (event.instructionChars !== undefined)
|
|
143
|
+
totalChars += event.instructionChars;
|
|
144
|
+
if (event.prefixHash !== undefined)
|
|
145
|
+
prefixHashes.add(event.prefixHash);
|
|
146
|
+
}
|
|
147
|
+
if (event.prefixHash !== undefined) {
|
|
148
|
+
const agent = event.agent ?? "unknown";
|
|
149
|
+
const model = `${event.provider}/${event.model}`;
|
|
150
|
+
const variant = event.variant ?? undefined;
|
|
151
|
+
const key = `${event.prefixHash}\u0000${agent}\u0000${model}\u0000${variant ?? "-"}`;
|
|
152
|
+
const group = groups.get(key);
|
|
153
|
+
if (group === undefined) {
|
|
154
|
+
groups.set(key, {
|
|
155
|
+
prefixHash: event.prefixHash,
|
|
156
|
+
agent,
|
|
157
|
+
model,
|
|
158
|
+
variant,
|
|
159
|
+
sessions: new Set([event.sessionId]),
|
|
160
|
+
calls: 1,
|
|
161
|
+
uncachedInputTokens: event.inputTokens,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
group.sessions.add(event.sessionId);
|
|
166
|
+
group.calls += 1;
|
|
167
|
+
group.uncachedInputTokens += event.inputTokens;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const sharedPrefixes = [];
|
|
172
|
+
for (const group of groups.values()) {
|
|
173
|
+
if (group.sessions.size < 2)
|
|
174
|
+
continue;
|
|
175
|
+
sharedPrefixes.push({
|
|
176
|
+
prefixHash: group.prefixHash,
|
|
177
|
+
agent: group.agent,
|
|
178
|
+
model: group.model,
|
|
179
|
+
...(group.variant === undefined ? {} : { variant: group.variant }),
|
|
180
|
+
sessions: group.sessions.size,
|
|
181
|
+
calls: group.calls,
|
|
182
|
+
uncachedInputTokens: group.uncachedInputTokens,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
sharedPrefixes.sort((a, b) => {
|
|
186
|
+
if (a.uncachedInputTokens !== b.uncachedInputTokens) {
|
|
187
|
+
return b.uncachedInputTokens - a.uncachedInputTokens;
|
|
188
|
+
}
|
|
189
|
+
return asc(a.prefixHash, b.prefixHash);
|
|
190
|
+
});
|
|
191
|
+
return {
|
|
192
|
+
sessions: seenSessions.size,
|
|
193
|
+
totalChars,
|
|
194
|
+
distinctPrefixHashes: prefixHashes.size,
|
|
195
|
+
sharedPrefixes,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/analyze/cache.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,sEAAsE;AACtE,SAAS,GAAG,CAAC,CAAS,EAAE,CAAS;IAC/B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAaD,SAAS,QAAQ;IACf,OAAO;QACL,KAAK,EAAE,CAAC;QACR,WAAW,EAAE,CAAC;QACd,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,CAAC;QACd,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,CAAC;KACrB,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,GAAQ,EAAE,KAAiB;IAC3C,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC;IACf,GAAG,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC;IACrC,GAAG,CAAC,UAAU,IAAI,KAAK,CAAC,eAAe,CAAC;IACxC,GAAG,CAAC,aAAa,IAAI,KAAK,CAAC,kBAAkB,CAAC;IAC9C,GAAG,CAAC,aAAa,IAAI,KAAK,CAAC,kBAAkB,CAAC;IAC9C,GAAG,CAAC,WAAW,IAAI,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,kBAAkB,CAAC;IACvE,GAAG,CAAC,gBAAgB,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;IAC7C,GAAG,CAAC,iBAAiB,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;AACjD,CAAC;AAED,SAAS,OAAO,CAAC,GAIhB;IACC,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,GAAG,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,WAAW,CAAC;IACrE,OAAO,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,GAAG,SAAS,CAAC;AAC7D,CAAC;AAED,SAAS,UAAU,CAAC,MAAoB;IACtC,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IACvB,KAAK,MAAM,KAAK,IAAI,MAAM;QAAE,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACjD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,GAAW,EAAE,MAAoB;IACpD,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAE/B,0EAA0E;IAC1E,yEAAyE;IACzE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1C,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;QACZ,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC;QAC5D,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,WAAW,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;YAAE,aAAa,GAAG,SAAS,GAAG,WAAW,CAAC;IAC1F,CAAC;IAED,OAAO;QACL,GAAG;QACH,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC;QACrB,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;QACxC,aAAa;KACd,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,CAAiB,EAAE,CAAiB;IACnE,MAAM,MAAM,GAAG,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,iBAAiB,CAAC;IACxD,MAAM,MAAM,GAAG,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,iBAAiB,CAAC;IACxD,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,MAAM,GAAG,MAAM,CAAC;IAC9C,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,SAAS,CAAC,GAA8B;IAC/C,MAAM,IAAI,GAAqB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,GAAG;QAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IACrE,OAAO,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAoB,EAAE,UAA6B,EAAE;IAC9E,MAAM,IAAI,GAAY,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC;IAE5C,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC;IAC1B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;IAChD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAwB,CAAC;IACnD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAC;IAClD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;IAE9C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACxB,MAAM,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;QAC3D,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9F,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAEpE,OAAO;QACL,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC;QACxB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,eAAe,EAAE,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,cAAc;QACrE,cAAc,EAAE,MAAM,CAAC,WAAW,GAAG,CAAC;QACtC,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC;QAC3B,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC;QACjC,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC;QAC/B,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC;QACvB,kBAAkB,EAAE,oBAAoB,CAAC,MAAM,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,GAA8B,EAAE,GAAW,EAAE,KAAiB;IAC5E,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;;QACzC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,MAAoB;IAChD,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,IAAI,UAAU,GAAG,CAAC,CAAC;IAWnB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;IAExC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,eAAe,GAAG,KAAK,CAAC,gBAAgB,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC;QAC/F,IAAI,eAAe,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1D,wEAAwE;YACxE,uDAAuD;YACvD,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAClC,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS;gBAAE,UAAU,IAAI,KAAK,CAAC,gBAAgB,CAAC;YAC/E,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;gBAAE,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACzE,CAAC;QAED,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,SAAS,CAAC;YACvC,MAAM,KAAK,GAAG,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,SAAS,CAAC;YAC3C,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,UAAU,SAAS,KAAK,SAAS,KAAK,SAAS,OAAO,IAAI,GAAG,EAAE,CAAC;YACrF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;oBACd,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,KAAK;oBACL,KAAK;oBACL,OAAO;oBACP,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;oBACpC,KAAK,EAAE,CAAC;oBACR,mBAAmB,EAAE,KAAK,CAAC,WAAW;iBACvC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACpC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;gBACjB,KAAK,CAAC,mBAAmB,IAAI,KAAK,CAAC,WAAW,CAAC;YACjD,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,cAAc,GAAmB,EAAE,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC;YAAE,SAAS;QACtC,cAAc,CAAC,IAAI,CAAC;YAClB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YAClE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI;YAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;SAC/C,CAAC,CAAC;IACL,CAAC;IACD,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC3B,IAAI,CAAC,CAAC,mBAAmB,KAAK,CAAC,CAAC,mBAAmB,EAAE,CAAC;YACpD,OAAO,CAAC,CAAC,mBAAmB,GAAG,CAAC,CAAC,mBAAmB,CAAC;QACvD,CAAC;QACD,OAAO,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,QAAQ,EAAE,YAAY,CAAC,IAAI;QAC3B,UAAU;QACV,oBAAoB,EAAE,YAAY,CAAC,IAAI;QACvC,cAAc;KACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Day bucketing.
|
|
3
|
+
*
|
|
4
|
+
* UTC is the default. For non-UTC zones the local calendar day comes from
|
|
5
|
+
* `Intl.DateTimeFormat.formatToParts`, never from slicing the ISO string: a
|
|
6
|
+
* slice is only correct when the string is already in the bucketing zone.
|
|
7
|
+
*/
|
|
8
|
+
import type { DayZone } from "../domain/analysis.js";
|
|
9
|
+
/** The calendar day one timestamp falls on in `zone`, as `YYYY-MM-DD`. */
|
|
10
|
+
export declare function dayKey(iso: string, zone?: DayZone): string;
|
|
11
|
+
//# sourceMappingURL=day.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"day.d.ts","sourceRoot":"","sources":["../../src/analyze/day.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAuBrD,0EAA0E;AAC1E,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,OAAe,GAAG,MAAM,CAejE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const formatters = new Map();
|
|
2
|
+
function pad(part) {
|
|
3
|
+
return part < 10 ? `0${part}` : String(part);
|
|
4
|
+
}
|
|
5
|
+
function formatterFor(zone) {
|
|
6
|
+
const cached = formatters.get(zone);
|
|
7
|
+
if (cached !== undefined)
|
|
8
|
+
return cached;
|
|
9
|
+
// en-CA renders year-month-day, so the parts are unambiguous.
|
|
10
|
+
const created = new Intl.DateTimeFormat("en-CA", {
|
|
11
|
+
timeZone: zone === "local" ? undefined : zone,
|
|
12
|
+
year: "numeric",
|
|
13
|
+
month: "2-digit",
|
|
14
|
+
day: "2-digit",
|
|
15
|
+
});
|
|
16
|
+
formatters.set(zone, created);
|
|
17
|
+
return created;
|
|
18
|
+
}
|
|
19
|
+
/** The calendar day one timestamp falls on in `zone`, as `YYYY-MM-DD`. */
|
|
20
|
+
export function dayKey(iso, zone = "UTC") {
|
|
21
|
+
const ms = Date.parse(iso);
|
|
22
|
+
if (!Number.isFinite(ms))
|
|
23
|
+
return "unknown";
|
|
24
|
+
if (zone === "UTC") {
|
|
25
|
+
// The UTC day comes straight from the parsed instant.
|
|
26
|
+
const date = new Date(ms);
|
|
27
|
+
return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`;
|
|
28
|
+
}
|
|
29
|
+
const parts = formatterFor(zone).formatToParts(ms);
|
|
30
|
+
const read = (type) => parts.find((part) => part.type === type)?.value ?? "";
|
|
31
|
+
return `${read("year")}-${read("month")}-${read("day")}`;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=day.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"day.js","sourceRoot":"","sources":["../../src/analyze/day.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,GAAG,IAAI,GAAG,EAA+B,CAAC;AAE1D,SAAS,GAAG,CAAC,IAAY;IACvB,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAExC,8DAA8D;IAC9D,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QAC/C,QAAQ,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;QAC7C,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;KACf,CAAC,CAAC;IACH,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,MAAM,CAAC,GAAW,EAAE,OAAgB,KAAK;IACvD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAE3C,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,sDAAsD;QACtD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1B,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;IAC7F,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,CAAC,IAAkC,EAAU,EAAE,CAC1D,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;IAExD,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyze/findings/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding detectors and thresholds shared by tests and the rule registry.
|
|
3
|
+
*
|
|
4
|
+
* The detector list itself now lives in `analyze/rules.ts` so rule metadata and
|
|
5
|
+
* detector wiring stay together.
|
|
6
|
+
*/
|
|
7
|
+
import { detectRepeatToolCalls } from "./repeat-tool-calls.js";
|
|
8
|
+
import { detectRepeatedContext, REPEATED_CONTEXT_THRESHOLDS } from "./repeated-context.js";
|
|
9
|
+
export { detectRepeatToolCalls } from "./repeat-tool-calls.js";
|
|
10
|
+
export { detectRepeatedContext, REPEATED_CONTEXT_THRESHOLDS } from "./repeated-context.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyze/findings/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAE3F,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repeated-read detector: the same read-only tool call made again in one
|
|
3
|
+
* context, with nothing changed in between.
|
|
4
|
+
*
|
|
5
|
+
* A duplicate is not one extra read: it is written into the cacheable prefix
|
|
6
|
+
* and re-read by every later call in the context, so it compounds. An edit to
|
|
7
|
+
* the file, or any change at all for a search, makes the next read fresh. A
|
|
8
|
+
* subagent starts with an empty context, so its reads are not repeats of the
|
|
9
|
+
* parent; neither is a read after a compaction.
|
|
10
|
+
*
|
|
11
|
+
* Unknown tools count as mutating, so duplicates are under-counted, never
|
|
12
|
+
* over-counted (Phase 3 plan, Step 6).
|
|
13
|
+
*/
|
|
14
|
+
import type { DetectionContext, FindingDraft } from "../../domain/finding.js";
|
|
15
|
+
export declare function detectRepeatToolCalls(context: DetectionContext): readonly FindingDraft[];
|
|
16
|
+
//# sourceMappingURL=repeat-tool-calls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repeat-tool-calls.d.ts","sourceRoot":"","sources":["../../../src/analyze/findings/repeat-tool-calls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAqF9E,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,YAAY,EAAE,CA+GxF"}
|