@agent-finops/core 0.8.0 → 0.9.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/README.md +5 -3
- package/dist/actionPlanner.d.ts +140 -0
- package/dist/actionPlanner.js +938 -0
- package/dist/actionVerification.d.ts +1240 -0
- package/dist/actionVerification.js +1028 -0
- package/dist/activitySnapshot.d.ts +101 -9
- package/dist/activitySnapshot.js +145 -6
- package/dist/activitySnapshotCache.d.ts +8 -1
- package/dist/activitySnapshotCache.js +103 -7
- package/dist/agentEconomicsReceipt.d.ts +58 -58
- package/dist/analyze.js +3 -1
- package/dist/cutList.js +1 -1
- package/dist/glance.d.ts +30 -2
- package/dist/glance.js +265 -84
- package/dist/index.d.ts +11 -2
- package/dist/index.js +10 -1
- package/dist/insights.js +3 -1
- package/dist/localAgentFormats/gemini.js +2 -2
- package/dist/localAgentFormats/registry.js +6 -2
- package/dist/localAgentFormats/runtimeRegistry.js +5 -2
- package/dist/localAgentFormats/types.d.ts +2 -1
- package/dist/localAgentLogs.d.ts +362 -3
- package/dist/localAgentLogs.js +1964 -165
- package/dist/modelPricing.d.ts +1 -1
- package/dist/modelPricing.js +4 -1
- package/dist/planMath.js +12 -7
- package/dist/projectEconomics.d.ts +617 -0
- package/dist/projectEconomics.js +620 -0
- package/dist/projectEconomicsBuilder.d.ts +89 -0
- package/dist/projectEconomicsBuilder.js +473 -0
- package/dist/projectIndexStore.d.ts +545 -0
- package/dist/projectIndexStore.js +606 -0
- package/dist/providerConnectors.d.ts +59 -1
- package/dist/providerConnectors.js +192 -12
- package/dist/qualitativeIndexCache.d.ts +494 -0
- package/dist/qualitativeIndexCache.js +930 -0
- package/dist/resultCard.d.ts +350 -0
- package/dist/resultCard.js +604 -0
- package/dist/runtimeCommands.d.ts +21 -0
- package/dist/runtimeCommands.js +27 -0
- package/dist/scanGuard.d.ts +3 -1
- package/dist/scanGuard.js +164 -4
- package/dist/schema.d.ts +31 -31
- package/dist/sessionVitals.d.ts +145 -0
- package/dist/sessionVitals.js +521 -0
- package/dist/sourceRegistry.js +90 -52
- package/dist/toolInvocations.d.ts +40 -1
- package/dist/toolInvocations.js +101 -20
- package/package.json +1 -1
package/dist/glance.js
CHANGED
|
@@ -3,6 +3,7 @@ import { canPriceTokenUsageAtScope, estimateTokenCostUsd, PRICING_TABLE_AS_OF }
|
|
|
3
3
|
import { subscriptionPlans } from "./planMath.js";
|
|
4
4
|
import { buildContextHealth } from "./contextHealth.js";
|
|
5
5
|
import { localAgentFormatDescriptors, localAgentFormatSupports } from "./localAgentFormats/registry.js";
|
|
6
|
+
import { aibillImproveCommandV0 } from "./runtimeCommands.js";
|
|
6
7
|
const HOUR_MS = 60 * 60 * 1_000;
|
|
7
8
|
const DAY_MS = 24 * HOUR_MS;
|
|
8
9
|
/**
|
|
@@ -45,7 +46,7 @@ export function buildUsageGlance(calls, options = {}) {
|
|
|
45
46
|
: null;
|
|
46
47
|
const limitCalls = sanitizeStringMetadata(options.limitCalls ?? safeCalls)
|
|
47
48
|
.filter((call) => localAgentFormatSupports(call.agent, "rateLimits"));
|
|
48
|
-
const limits = latestLimits(limitCalls, now).map(({ agent, window, observedAt }) => toGlanceLimit(agent, window, observedAt));
|
|
49
|
+
const limits = latestLimits(limitCalls, now).map(({ agent, window, observedAt }) => toGlanceLimit(agent, window, observedAt, now));
|
|
49
50
|
const windowStart = now.getTime() - focusWindowDays * DAY_MS;
|
|
50
51
|
const windowCalls = safeCalls.filter((call) => Date.parse(call.timestamp) >= windowStart);
|
|
51
52
|
const windowSessions = groupSessions(windowCalls);
|
|
@@ -55,20 +56,41 @@ export function buildUsageGlance(calls, options = {}) {
|
|
|
55
56
|
const focusSessions = latest?.project && !isGenericProject(latest.project)
|
|
56
57
|
? windowSessions.filter((session) => session.project === latest.project)
|
|
57
58
|
: windowSessions;
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
59
|
+
const qualitativeCoverage = options.qualitativeCoverage ?? {
|
|
60
|
+
status: "complete",
|
|
61
|
+
selectedFiles: options.filesParsed ?? 0,
|
|
62
|
+
readCompletely: options.filesParsed ?? 0,
|
|
63
|
+
skippedForBudget: 0
|
|
64
|
+
};
|
|
65
|
+
const qualitativeComplete = qualitativeCoverage.status === "complete";
|
|
66
|
+
const focus = qualitativeComplete
|
|
67
|
+
? buildMainFocus(focusSessions, focusWindowDays, now)
|
|
68
|
+
: null;
|
|
69
|
+
const baseSessionHealth = suppliedContextHealth ?? buildContextHealth({ calls: safeCalls, now });
|
|
70
|
+
const sessionHealth = {
|
|
71
|
+
...baseSessionHealth,
|
|
72
|
+
qualitativeCoverage
|
|
73
|
+
};
|
|
74
|
+
const anomaly = qualitativeComplete ? anomalyFromContextHealth(sessionHealth) : null;
|
|
75
|
+
const primaryAction = qualitativeComplete
|
|
76
|
+
? buildPrimaryAction({
|
|
77
|
+
currentSession,
|
|
78
|
+
focus,
|
|
79
|
+
limits,
|
|
80
|
+
sessionHealth,
|
|
81
|
+
generatedAt: now.toISOString(),
|
|
82
|
+
filesParsed: options.filesParsed ?? 0
|
|
83
|
+
})
|
|
84
|
+
: buildCoverageLimitedPrimaryAction({
|
|
85
|
+
currentSession,
|
|
86
|
+
sessionHealth,
|
|
87
|
+
coverage: qualitativeCoverage
|
|
88
|
+
});
|
|
89
|
+
const tokenExperiment = sanitizeActionVerificationProjection(options.actionVerificationProjection);
|
|
69
90
|
const detectedAgents = (options.detectedAgents ?? uniqueAgents(safeCalls))
|
|
70
91
|
.filter((agent) => localAgentFormatSupports(agent, "glance"));
|
|
71
|
-
const
|
|
92
|
+
const agentsWithCurrentLimits = new Set(limits.filter((limit) => limit.freshness === "current").map((limit) => limit.agent));
|
|
93
|
+
const agentsWithStaleLimits = new Set(limits.filter((limit) => limit.freshness === "stale").map((limit) => limit.agent));
|
|
72
94
|
const limitAgents = uniqueAgents(limitCalls.filter((call) => call.rateLimits));
|
|
73
95
|
const reportedWindows = (agent) => ([...new Set(limits
|
|
74
96
|
.filter((limit) => limit.agent === agent)
|
|
@@ -77,22 +99,30 @@ export function buildUsageGlance(calls, options = {}) {
|
|
|
77
99
|
"Session value is an API-equivalent estimate from transcript token counts, not an invoice or subscription charge.",
|
|
78
100
|
"A detected monthly subscription changes the interpretation, not the token math: the API-equivalent amount is usage priced at list rates, not incremental spend or business outcome value.",
|
|
79
101
|
"Exhaustion time is a pace projection; remaining percentage and reset time are provider-reported only when embedded in a transcript.",
|
|
102
|
+
"A five-hour percentage is current for at most five hours after observation; a weekly percentage is current for at most 24 hours. Older transcript evidence is labeled stale and never drives an action.",
|
|
80
103
|
"Main focus is a local summary of observed human prompts and tool activity, not elapsed time or spend; raw prompts are not returned.",
|
|
81
104
|
"The primary action combines Context Health, Main focus, and reported runway locally. It only provides a copyable handoff prompt and never runs an agent automatically.",
|
|
105
|
+
"A token-test percentage compares matched local session cohorts guarded by explicit quality evidence; it is not certified savings, verified outcome ROI, or a provider bill.",
|
|
82
106
|
"Claude Code transcripts do not report plan headroom. Missing limits remain unavailable instead of being inferred.",
|
|
83
|
-
"Cursor and GitHub Copilot require their provider connections because their local chat stores are not treated as authoritative billing transcripts."
|
|
107
|
+
"Cursor and GitHub Copilot require their provider connections because their local chat stores are not treated as authoritative billing transcripts.",
|
|
108
|
+
...(qualitativeComplete ? [] : [
|
|
109
|
+
"Main focus, anomaly, and context-change handoff are unavailable because the bounded qualitative index is incomplete; no global driver was inferred from a selected subset."
|
|
110
|
+
])
|
|
84
111
|
];
|
|
85
112
|
return {
|
|
86
113
|
dataMode: "local_transcripts",
|
|
87
114
|
generatedAt: now.toISOString(),
|
|
88
115
|
coverage: {
|
|
89
116
|
filesParsed: options.filesParsed ?? 0,
|
|
117
|
+
qualitative: qualitativeCoverage,
|
|
90
118
|
supportedTranscriptAgents: supportedFormats.map((descriptor) => descriptor.id),
|
|
91
119
|
detectedAgents,
|
|
92
120
|
rateLimitMetadata: supportedFormats.map((descriptor) => ({
|
|
93
121
|
agent: descriptor.id,
|
|
94
122
|
status: descriptor.capabilities.rateLimits
|
|
95
|
-
?
|
|
123
|
+
? agentsWithCurrentLimits.has(descriptor.id)
|
|
124
|
+
? "reported"
|
|
125
|
+
: agentsWithStaleLimits.has(descriptor.id) ? "stale" : "not_seen"
|
|
96
126
|
: "not_reported_by_transcript",
|
|
97
127
|
windowsReported: reportedWindows(descriptor.id)
|
|
98
128
|
})),
|
|
@@ -142,6 +172,14 @@ export function buildUsageGlance(calls, options = {}) {
|
|
|
142
172
|
execution: "copy_prompt",
|
|
143
173
|
automaticExecution: false
|
|
144
174
|
},
|
|
175
|
+
tokenExperiment: {
|
|
176
|
+
source: tokenExperiment
|
|
177
|
+
? "canonical_action_verification_projection"
|
|
178
|
+
: "not_available",
|
|
179
|
+
calculation: "core_experiment_evaluator",
|
|
180
|
+
cohort: "matched_local_sessions",
|
|
181
|
+
automaticExecution: false
|
|
182
|
+
},
|
|
145
183
|
network: {
|
|
146
184
|
uploaded: false
|
|
147
185
|
}
|
|
@@ -153,9 +191,114 @@ export function buildUsageGlance(calls, options = {}) {
|
|
|
153
191
|
anomaly,
|
|
154
192
|
sessionHealth,
|
|
155
193
|
primaryAction,
|
|
194
|
+
...(tokenExperiment ? { tokenExperiment } : {}),
|
|
156
195
|
caveats
|
|
157
196
|
};
|
|
158
197
|
}
|
|
198
|
+
function buildCoverageLimitedPrimaryAction(input) {
|
|
199
|
+
const project = safeActionMetadata(input.currentSession?.project, 80);
|
|
200
|
+
const status = input.coverage.status === "partial" ? "partial" : "not available";
|
|
201
|
+
return {
|
|
202
|
+
kind: "session_handoff",
|
|
203
|
+
intent: "inspect_current_work",
|
|
204
|
+
label: project ? `Refresh evidence · ${project}` : "Refresh evidence",
|
|
205
|
+
detail: `Main focus unavailable · qualitative index ${status}`,
|
|
206
|
+
...(project ? { project } : {}),
|
|
207
|
+
agentPrompt: [
|
|
208
|
+
"aibill's bounded qualitative evidence is incomplete.",
|
|
209
|
+
"Do not infer a global main focus, waste cause, or context change from the selected subset.",
|
|
210
|
+
`Run \`${aibillImproveCommandV0()}\` from the exact project root to refresh the private index, then review the new evidence before editing.`
|
|
211
|
+
].join("\n"),
|
|
212
|
+
source: "context_health_focus_and_reported_runway",
|
|
213
|
+
confidence: "low",
|
|
214
|
+
execution: "copy_prompt",
|
|
215
|
+
requiresUserConfirmation: true,
|
|
216
|
+
evidenceWindowDays: input.sessionHealth.deadContext.windowDays
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
const actionVerificationStates = new Set([
|
|
220
|
+
"collect_baseline",
|
|
221
|
+
"approve_one_change",
|
|
222
|
+
"collect_post_change",
|
|
223
|
+
"review_measured_result",
|
|
224
|
+
"rollback",
|
|
225
|
+
"resolve_evidence",
|
|
226
|
+
"rolled_back",
|
|
227
|
+
"cancelled"
|
|
228
|
+
]);
|
|
229
|
+
const actionVerificationTones = new Set([
|
|
230
|
+
"neutral",
|
|
231
|
+
"attention",
|
|
232
|
+
"positive",
|
|
233
|
+
"negative"
|
|
234
|
+
]);
|
|
235
|
+
const actionVerificationEvidenceLabels = new Set([
|
|
236
|
+
"calculated",
|
|
237
|
+
"missing"
|
|
238
|
+
]);
|
|
239
|
+
const actionVerificationQualityLabels = new Set([
|
|
240
|
+
"held",
|
|
241
|
+
"regressed",
|
|
242
|
+
"insufficient"
|
|
243
|
+
]);
|
|
244
|
+
const actionVerificationQualityEvidence = new Set([
|
|
245
|
+
"verified",
|
|
246
|
+
"observed",
|
|
247
|
+
"user_declared",
|
|
248
|
+
"missing"
|
|
249
|
+
]);
|
|
250
|
+
const experimentIdPattern = /^tre_v0_[a-f0-9]{64}$/;
|
|
251
|
+
const findingIdPattern = /^wf_v0_[a-f0-9]{64}$/;
|
|
252
|
+
const candidateKeyPattern = /^wfc_v0_[a-f0-9]{64}$/;
|
|
253
|
+
/**
|
|
254
|
+
* Treat the optional adapter input as untrusted at runtime. A malformed or
|
|
255
|
+
* internally inconsistent projection is omitted instead of becoming a stale
|
|
256
|
+
* or invented Glance claim. This function deliberately does not derive any
|
|
257
|
+
* experiment result.
|
|
258
|
+
*/
|
|
259
|
+
function sanitizeActionVerificationProjection(input) {
|
|
260
|
+
if (!input || typeof input !== "object")
|
|
261
|
+
return undefined;
|
|
262
|
+
const safe = sanitizeStringMetadata(input);
|
|
263
|
+
if (safe.schemaVersion !== 0 ||
|
|
264
|
+
!experimentIdPattern.test(safe.experimentId) ||
|
|
265
|
+
!findingIdPattern.test(safe.findingId) ||
|
|
266
|
+
!candidateKeyPattern.test(safe.candidateKey) ||
|
|
267
|
+
!actionVerificationStates.has(safe.state) ||
|
|
268
|
+
!actionVerificationTones.has(safe.tone) ||
|
|
269
|
+
!actionVerificationEvidenceLabels.has(safe.evidenceLabel) ||
|
|
270
|
+
!actionVerificationQualityLabels.has(safe.qualityLabel) ||
|
|
271
|
+
!actionVerificationQualityEvidence.has(safe.qualityEvidence) ||
|
|
272
|
+
!isSafeExperimentCount(safe.baselineSessions) ||
|
|
273
|
+
!isSafeExperimentCount(safe.postChangeSessions) ||
|
|
274
|
+
!isSafeExperimentCount(safe.minimumSessions) ||
|
|
275
|
+
safe.minimumSessions < 1 ||
|
|
276
|
+
(safe.reductionPercent !== null && (!Number.isFinite(safe.reductionPercent) ||
|
|
277
|
+
safe.reductionPercent > 100 ||
|
|
278
|
+
safe.reductionPercent < -1_000_000))) {
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
const measured = safe.state === "review_measured_result";
|
|
282
|
+
const claimQualityEvidence = safe.qualityEvidence === "verified" ||
|
|
283
|
+
safe.qualityEvidence === "observed" ||
|
|
284
|
+
safe.qualityEvidence === "user_declared";
|
|
285
|
+
if (safe.reductionPercent !== null) {
|
|
286
|
+
const claimEvidenceIsComplete = safe.evidenceLabel === "calculated" &&
|
|
287
|
+
safe.qualityLabel === "held" &&
|
|
288
|
+
claimQualityEvidence;
|
|
289
|
+
const signMatchesState = (measured && safe.reductionPercent >= 0) ||
|
|
290
|
+
(safe.state === "rollback" && safe.reductionPercent < 0);
|
|
291
|
+
if (!claimEvidenceIsComplete || !signMatchesState)
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
else if (measured) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
return safe;
|
|
298
|
+
}
|
|
299
|
+
function isSafeExperimentCount(value) {
|
|
300
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
301
|
+
}
|
|
159
302
|
function toGlancePlan(agent, detectedPlans) {
|
|
160
303
|
const detected = detectedPlans.find((plan) => plan.agent === agent);
|
|
161
304
|
if (!detected)
|
|
@@ -267,8 +410,11 @@ function latestLimits(calls, now) {
|
|
|
267
410
|
return [...byWindow.values()].sort((left, right) => (order[left.window.kind] - order[right.window.kind] ||
|
|
268
411
|
left.agent.localeCompare(right.agent)));
|
|
269
412
|
}
|
|
270
|
-
function toGlanceLimit(agent, window, observedAt) {
|
|
271
|
-
const
|
|
413
|
+
function toGlanceLimit(agent, window, observedAt, now) {
|
|
414
|
+
const freshness = limitEvidenceFreshness(window, observedAt, now);
|
|
415
|
+
const projection = freshness === "current"
|
|
416
|
+
? projectExhaustion(window, observedAt, now)
|
|
417
|
+
: { at: null, beforeReset: false };
|
|
272
418
|
return {
|
|
273
419
|
agent,
|
|
274
420
|
kind: window.kind,
|
|
@@ -279,12 +425,13 @@ function toGlanceLimit(agent, window, observedAt) {
|
|
|
279
425
|
observedAt,
|
|
280
426
|
resetsAt: window.resetsAt,
|
|
281
427
|
source: "transcript_reported",
|
|
428
|
+
freshness,
|
|
282
429
|
projectedExhaustionAt: projection.at,
|
|
283
430
|
projectedToExhaustBeforeReset: projection.beforeReset,
|
|
284
431
|
projectionConfidence: "estimated"
|
|
285
432
|
};
|
|
286
433
|
}
|
|
287
|
-
function projectExhaustion(window, observedAt) {
|
|
434
|
+
function projectExhaustion(window, observedAt, now) {
|
|
288
435
|
const observedMs = Date.parse(observedAt);
|
|
289
436
|
const resetMs = Date.parse(window.resetsAt);
|
|
290
437
|
const windowStartMs = resetMs - window.windowMinutes * 60_000;
|
|
@@ -296,15 +443,27 @@ function projectExhaustion(window, observedAt) {
|
|
|
296
443
|
window.usedPercent <= 0) {
|
|
297
444
|
return { at: null, beforeReset: false };
|
|
298
445
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
446
|
+
// A current report at the limit is an observed exhausted state, not a
|
|
447
|
+
// forecast. Keep the projection empty so downstream copy cannot describe an
|
|
448
|
+
// already-exhausted window as something that merely "may" exhaust.
|
|
449
|
+
if (window.usedPercent >= 100)
|
|
450
|
+
return { at: null, beforeReset: false };
|
|
302
451
|
const remainingMs = elapsedMs * ((100 - window.usedPercent) / window.usedPercent);
|
|
303
452
|
const exhaustionMs = observedMs + remainingMs;
|
|
304
|
-
return exhaustionMs < resetMs
|
|
453
|
+
return exhaustionMs > now.getTime() && exhaustionMs < resetMs
|
|
305
454
|
? { at: new Date(exhaustionMs).toISOString(), beforeReset: true }
|
|
306
455
|
: { at: null, beforeReset: false };
|
|
307
456
|
}
|
|
457
|
+
function limitEvidenceFreshness(window, observedAt, now) {
|
|
458
|
+
const observedMs = Date.parse(observedAt);
|
|
459
|
+
const ageMs = now.getTime() - observedMs;
|
|
460
|
+
const windowMs = window.windowMinutes * 60_000;
|
|
461
|
+
const maximumAgeMs = Math.min(windowMs, DAY_MS);
|
|
462
|
+
return Number.isFinite(observedMs) && Number.isFinite(windowMs) && windowMs > 0 &&
|
|
463
|
+
ageMs >= 0 && ageMs <= maximumAgeMs
|
|
464
|
+
? "current"
|
|
465
|
+
: "stale";
|
|
466
|
+
}
|
|
308
467
|
function buildMainFocus(sessions, windowDays, now) {
|
|
309
468
|
const candidates = sessions.map((session) => {
|
|
310
469
|
const rawActivity = session.activity ?? fallbackActivity(session);
|
|
@@ -449,8 +608,17 @@ function buildPrimaryAction(input) {
|
|
|
449
608
|
const project = safeActionMetadata(preferredProject, 80);
|
|
450
609
|
const focus = safeActionMetadata(input.focus?.summary, 120);
|
|
451
610
|
const focalFile = safeActionMetadata(input.focus?.file, 100);
|
|
611
|
+
const generatedAtMs = Date.parse(input.generatedAt);
|
|
612
|
+
const exhaustedLimit = input.limits
|
|
613
|
+
.filter((limit) => (limit.freshness === "current" &&
|
|
614
|
+
limit.usedPercent >= 100 &&
|
|
615
|
+
Date.parse(limit.resetsAt) > generatedAtMs))
|
|
616
|
+
.sort((left, right) => Date.parse(left.resetsAt) - Date.parse(right.resetsAt))[0];
|
|
452
617
|
const urgentLimit = input.limits
|
|
453
|
-
.filter((limit) => limit.
|
|
618
|
+
.filter((limit) => (limit.freshness === "current" &&
|
|
619
|
+
limit.projectedToExhaustBeforeReset &&
|
|
620
|
+
limit.projectedExhaustionAt !== null &&
|
|
621
|
+
Date.parse(limit.projectedExhaustionAt) > Date.parse(input.generatedAt)))
|
|
454
622
|
.sort((left, right) => left.remainingPercent - right.remainingPercent)[0];
|
|
455
623
|
const projectSuffix = project ? ` · ${project}` : "";
|
|
456
624
|
let intent;
|
|
@@ -458,68 +626,81 @@ function buildPrimaryAction(input) {
|
|
|
458
626
|
let detail;
|
|
459
627
|
let instruction;
|
|
460
628
|
let confidence = input.sessionHealth.confidence;
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
629
|
+
if (exhaustedLimit) {
|
|
630
|
+
intent = "protect_runway";
|
|
631
|
+
label = `Checkpoint${projectSuffix}`;
|
|
632
|
+
detail = `${limitActionName(exhaustedLimit)} is exhausted until reset`;
|
|
633
|
+
instruction = "Create a concise checkpoint for the observed focus and wait for the provider-reported reset before resuming work that requires this plan window.";
|
|
634
|
+
confidence = "high";
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
switch (input.sessionHealth.recommendation) {
|
|
638
|
+
case "start_fresh":
|
|
639
|
+
intent = "start_fresh";
|
|
640
|
+
label = `Start fresh${projectSuffix}`;
|
|
641
|
+
detail = focus
|
|
642
|
+
? `Carry “${focus}” into a clean session`
|
|
643
|
+
: "Carry only the concrete state you still need";
|
|
644
|
+
instruction = "Start a clean session and continue the observed focus after verifying the current repository state.";
|
|
645
|
+
break;
|
|
646
|
+
case "review_hooks":
|
|
647
|
+
intent = "review_context";
|
|
648
|
+
label = `Review context${projectSuffix}`;
|
|
649
|
+
detail = focus
|
|
650
|
+
? `Protect “${focus}” from unnecessary hook context`
|
|
651
|
+
: "Inspect configured hooks before removing anything";
|
|
652
|
+
instruction = "Review installed hook sources that affect this work. Do not remove or edit configuration without explicit user approval.";
|
|
653
|
+
break;
|
|
654
|
+
case "trim_dead_context":
|
|
655
|
+
intent = "trim_context";
|
|
656
|
+
label = `Trim context${projectSuffix}`;
|
|
657
|
+
detail = focus
|
|
658
|
+
? `Keep only context useful to “${focus}”`
|
|
659
|
+
: "Inspect unused loaded context before changing it";
|
|
660
|
+
instruction = "Identify loaded context that is unrelated to the observed focus. Recommend scoped changes, but do not remove anything without explicit user approval.";
|
|
661
|
+
break;
|
|
662
|
+
default:
|
|
663
|
+
if (urgentLimit) {
|
|
664
|
+
intent = "protect_runway";
|
|
665
|
+
label = `Checkpoint${projectSuffix}`;
|
|
666
|
+
detail = `${limitActionName(urgentLimit)} may exhaust before reset`;
|
|
667
|
+
instruction = "Create a concise checkpoint for the observed focus and prioritize the smallest verifiable next step before the reported plan window may be exhausted.";
|
|
668
|
+
confidence = "medium";
|
|
669
|
+
}
|
|
670
|
+
else if (focus &&
|
|
671
|
+
input.focus?.confidence !== "low" &&
|
|
672
|
+
input.currentSession?.status === "active") {
|
|
673
|
+
intent = "continue_focus";
|
|
674
|
+
label = `Continue${projectSuffix}`;
|
|
675
|
+
detail = focus;
|
|
676
|
+
instruction = "Continue the observed focus with the smallest verifiable next step.";
|
|
677
|
+
confidence = input.focus?.confidence ?? input.sessionHealth.confidence;
|
|
678
|
+
}
|
|
679
|
+
else if (focus && input.focus?.confidence !== "low") {
|
|
680
|
+
intent = "resume_focus";
|
|
681
|
+
label = `Resume${projectSuffix}`;
|
|
682
|
+
detail = focus;
|
|
683
|
+
instruction = "Resume the observed focus after checking what changed since the last local activity.";
|
|
684
|
+
confidence = input.focus?.confidence ?? input.sessionHealth.confidence;
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
intent = "inspect_current_work";
|
|
688
|
+
label = `Inspect current work${projectSuffix}`;
|
|
689
|
+
detail = "Verify the active task before making changes";
|
|
690
|
+
instruction = "Inspect the current repository and ask for the intended task if it cannot be established from local evidence.";
|
|
691
|
+
confidence = "low";
|
|
692
|
+
}
|
|
693
|
+
}
|
|
517
694
|
}
|
|
518
|
-
const runway =
|
|
519
|
-
? `${limitActionName(
|
|
520
|
-
:
|
|
521
|
-
?
|
|
522
|
-
:
|
|
695
|
+
const runway = exhaustedLimit
|
|
696
|
+
? `${limitActionName(exhaustedLimit)}: exhausted (0% remaining); provider-reported reset=${exhaustedLimit.resetsAt}; observed=${exhaustedLimit.observedAt}.`
|
|
697
|
+
: urgentLimit
|
|
698
|
+
? `${limitActionName(urgentLimit)}: ${roundPercent(urgentLimit.remainingPercent)}% remaining; locally projected exhaustion=${urgentLimit.projectedExhaustionAt ?? "unavailable"}; provider-reported reset=${urgentLimit.resetsAt}.`
|
|
699
|
+
: input.limits.some((limit) => limit.freshness === "current")
|
|
700
|
+
? "No transcript-reported plan window is currently projected to exhaust before reset."
|
|
701
|
+
: input.limits.some((limit) => limit.freshness === "stale")
|
|
702
|
+
? "Stale; the last transcript-reported plan window is too old to use as current runway. Refresh the agent limit before acting."
|
|
703
|
+
: "Not available; no plan window was reported in the local transcript.";
|
|
523
704
|
const reportedTotalEvidence = input.currentSession?.reportedTotalTokens === undefined
|
|
524
705
|
? ""
|
|
525
706
|
: `; provider-reported total tokens=${input.currentSession.reportedTotalTokens.toLocaleString("en-US")}; input/output breakdown unavailable`;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from "./analyze.js";
|
|
2
|
+
export * from "./actionPlanner.js";
|
|
3
|
+
export * from "./actionVerification.js";
|
|
2
4
|
export * from "./agentInventory.js";
|
|
3
5
|
export * from "./agentEconomicsReceipt.js";
|
|
4
6
|
export * from "./activitySnapshot.js";
|
|
@@ -12,16 +14,23 @@ export * from "./discovery.js";
|
|
|
12
14
|
export * from "./glance.js";
|
|
13
15
|
export * from "./toolInvocations.js";
|
|
14
16
|
export * from "./insights.js";
|
|
15
|
-
export { aggregateCalls, dedupeCumulativeSessionCalls, latestObservedWorkingDirectory, loadLocalAgentFinancialUsage, loadLocalAgentUsage, parseClaudeCodeTranscript, parseCodexRollout, sanitizeLocalActivityText } from "./localAgentLogs.js";
|
|
16
|
-
export type { LocalAgentActivity, LocalAgentCall, LocalAgentFinancialLogOptions, LocalAgentLogDiagnostic, LocalAgentLogDiagnosticCode, LocalAgentLogOptions, LocalAgentLogResult, LocalAgentRateLimitSnapshot, LocalAgentRateLimitWindow, LocalAgentSourceScan, LocalAgentTurnUsage } from "./localAgentLogs.js";
|
|
17
|
+
export { aggregateCalls, codexHeaderProbesPerScan, dedupeCumulativeSessionCalls, defaultStreamedBytesPerRun, hasCompleteQualitativeCoverage, hasExactSelectedQualitativeEvidence, latestObservedWorkingDirectory, loadLocalAgentActionEvidence, loadLocalAgentFinancialUsage, loadLocalAgentUsage, localAgentQualitativeParserVersion, parseClaudeCodeTranscript, parseCodexRollout, SAFE_QUALITATIVE_SCAN_POLICY, sanitizeLocalActivityText } from "./localAgentLogs.js";
|
|
18
|
+
export type { LocalAgentActivity, LocalAgentCall, LocalAgentCompletionEvidence, LocalAgentFinancialLogOptions, LocalAgentOwnershipIndexAdapter, LocalAgentOwnershipRecord, LocalAgentQualitativeIndexAdapter, LocalAgentQualitativeIndexKey, LocalAgentQualitativeIndexValue, LocalAgentQualitativeScanPolicy, LocalAgentStreamCheckpointAdapter, LocalAgentStreamCheckpointRecord, LocalAgentLogDiagnostic, LocalAgentLogDiagnosticCode, LocalAgentLogOptions, LocalAgentLogResult, LocalAgentRateLimitSnapshot, LocalAgentRateLimitWindow, LocalAgentSourceScan, LocalAgentTokenComponentEvidence, LocalAgentTurnUsage } from "./localAgentLogs.js";
|
|
17
19
|
export * from "./localAgentFormats/registry.js";
|
|
18
20
|
export type * from "./localAgentFormats/types.js";
|
|
19
21
|
export * from "./modelPricing.js";
|
|
20
22
|
export * from "./planDetection.js";
|
|
21
23
|
export * from "./planMath.js";
|
|
24
|
+
export * from "./projectEconomics.js";
|
|
25
|
+
export * from "./resultCard.js";
|
|
26
|
+
export * from "./projectEconomicsBuilder.js";
|
|
27
|
+
export * from "./qualitativeIndexCache.js";
|
|
28
|
+
export * from "./projectIndexStore.js";
|
|
29
|
+
export * from "./runtimeCommands.js";
|
|
22
30
|
export * from "./sampleData.js";
|
|
23
31
|
export * from "./scanGuard.js";
|
|
24
32
|
export * from "./schema.js";
|
|
33
|
+
export * from "./sessionVitals.js";
|
|
25
34
|
export * from "./sourceRegistry.js";
|
|
26
35
|
export * from "./sourceStatus.js";
|
|
27
36
|
export * from "./stateTrust.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from "./analyze.js";
|
|
2
|
+
export * from "./actionPlanner.js";
|
|
3
|
+
export * from "./actionVerification.js";
|
|
2
4
|
export * from "./agentInventory.js";
|
|
3
5
|
export * from "./agentEconomicsReceipt.js";
|
|
4
6
|
export * from "./activitySnapshot.js";
|
|
@@ -12,14 +14,21 @@ export * from "./discovery.js";
|
|
|
12
14
|
export * from "./glance.js";
|
|
13
15
|
export * from "./toolInvocations.js";
|
|
14
16
|
export * from "./insights.js";
|
|
15
|
-
export { aggregateCalls, dedupeCumulativeSessionCalls, latestObservedWorkingDirectory, loadLocalAgentFinancialUsage, loadLocalAgentUsage, parseClaudeCodeTranscript, parseCodexRollout, sanitizeLocalActivityText } from "./localAgentLogs.js";
|
|
17
|
+
export { aggregateCalls, codexHeaderProbesPerScan, dedupeCumulativeSessionCalls, defaultStreamedBytesPerRun, hasCompleteQualitativeCoverage, hasExactSelectedQualitativeEvidence, latestObservedWorkingDirectory, loadLocalAgentActionEvidence, loadLocalAgentFinancialUsage, loadLocalAgentUsage, localAgentQualitativeParserVersion, parseClaudeCodeTranscript, parseCodexRollout, SAFE_QUALITATIVE_SCAN_POLICY, sanitizeLocalActivityText } from "./localAgentLogs.js";
|
|
16
18
|
export * from "./localAgentFormats/registry.js";
|
|
17
19
|
export * from "./modelPricing.js";
|
|
18
20
|
export * from "./planDetection.js";
|
|
19
21
|
export * from "./planMath.js";
|
|
22
|
+
export * from "./projectEconomics.js";
|
|
23
|
+
export * from "./resultCard.js";
|
|
24
|
+
export * from "./projectEconomicsBuilder.js";
|
|
25
|
+
export * from "./qualitativeIndexCache.js";
|
|
26
|
+
export * from "./projectIndexStore.js";
|
|
27
|
+
export * from "./runtimeCommands.js";
|
|
20
28
|
export * from "./sampleData.js";
|
|
21
29
|
export * from "./scanGuard.js";
|
|
22
30
|
export * from "./schema.js";
|
|
31
|
+
export * from "./sessionVitals.js";
|
|
23
32
|
export * from "./sourceRegistry.js";
|
|
24
33
|
export * from "./sourceStatus.js";
|
|
25
34
|
export * from "./stateTrust.js";
|
package/dist/insights.js
CHANGED
|
@@ -178,6 +178,8 @@ function combinedConfidence(confidences) {
|
|
|
178
178
|
return confidences.reduce((lowest, current) => confidenceRank[current] > confidenceRank[lowest] ? current : lowest);
|
|
179
179
|
}
|
|
180
180
|
function formatUsd(value) {
|
|
181
|
+
if (value > 0 && value < 0.01)
|
|
182
|
+
return "<$0.01";
|
|
181
183
|
return `$${value.toFixed(2)}`;
|
|
182
184
|
}
|
|
183
185
|
function formatMultiplier(value) {
|
|
@@ -201,6 +203,6 @@ function stableSuffix(value) {
|
|
|
201
203
|
return (hash >>> 0).toString(36);
|
|
202
204
|
}
|
|
203
205
|
function roundMoney(value) {
|
|
204
|
-
return Math.round(value *
|
|
206
|
+
return Math.round(value * 10_000) / 10_000;
|
|
205
207
|
}
|
|
206
208
|
//# sourceMappingURL=insights.js.map
|
|
@@ -176,8 +176,8 @@ function processMessage(message, state) {
|
|
|
176
176
|
state.diagnostics.add("missing_timestamp");
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
// Window-blind on purpose (financial cache correctness): the loader's
|
|
180
|
+
// final timestamp filter performs all narrowing.
|
|
181
181
|
const parsedTokens = parseTokens(message.tokens);
|
|
182
182
|
const metadata = mergeMetadata(state.metadata, message);
|
|
183
183
|
const attribution = explicitAttribution(message, metadata) ??
|
|
@@ -46,6 +46,7 @@ const descriptors = [
|
|
|
46
46
|
fieldsRead: [
|
|
47
47
|
"timestamp, model, and token-usage components",
|
|
48
48
|
"session and working-directory metadata for local deduplication and attribution",
|
|
49
|
+
"explicit system/turn_duration work-unit completion markers and transcript version strings when present",
|
|
49
50
|
"human-prompt and tool metadata for privacy-reduced local activity summaries"
|
|
50
51
|
],
|
|
51
52
|
verified: [
|
|
@@ -65,7 +66,8 @@ const descriptors = [
|
|
|
65
66
|
],
|
|
66
67
|
limitations: [
|
|
67
68
|
"Malformed lines are skipped and reported.",
|
|
68
|
-
"Incomplete token shapes remain unpriced with missing financial evidence instead of becoming $0."
|
|
69
|
+
"Incomplete token shapes remain unpriced with missing financial evidence instead of becoming $0.",
|
|
70
|
+
"A system/turn_duration marker proves only that the latest observed turn completed; it does not prove permanent transcript closure, and missing or inconsistent completion evidence stays ineligible for automatic before/after cohorts."
|
|
69
71
|
]
|
|
70
72
|
},
|
|
71
73
|
fixtures: ["claude-code-v1"]
|
|
@@ -115,6 +117,7 @@ const descriptors = [
|
|
|
115
117
|
],
|
|
116
118
|
fieldsRead: [
|
|
117
119
|
"session metadata, timestamps, model, and cumulative/last-turn token usage",
|
|
120
|
+
"explicit event_msg/task_complete work-unit completion markers and session_meta cli_version strings when present",
|
|
118
121
|
"transcript-reported rate-limit windows when present",
|
|
119
122
|
"tool-call metadata for local attribution and optional privacy-safe invocation counts"
|
|
120
123
|
],
|
|
@@ -135,7 +138,8 @@ const descriptors = [
|
|
|
135
138
|
],
|
|
136
139
|
limitations: [
|
|
137
140
|
"Only rollout-*.jsonl files are parsed as Codex sessions.",
|
|
138
|
-
"Incomplete, regressing, or total-only token shapes remain unpriced with missing financial evidence."
|
|
141
|
+
"Incomplete, regressing, or total-only token shapes remain unpriced with missing financial evidence.",
|
|
142
|
+
"An event_msg/task_complete marker proves only that the latest observed task completed; it does not prove permanent transcript closure, and missing or inconsistent completion evidence stays ineligible for automatic before/after cohorts."
|
|
139
143
|
]
|
|
140
144
|
},
|
|
141
145
|
fixtures: ["codex-v1"]
|
|
@@ -24,9 +24,12 @@ const runtimes = [
|
|
|
24
24
|
const collector = collectInvocationEvidence
|
|
25
25
|
? createCodexInvocationCollector(sinceMs)
|
|
26
26
|
: undefined;
|
|
27
|
+
const calls = parseCodexRollout(content, collector?.consume, onDiagnostic);
|
|
28
|
+
const invocationFile = collector?.finish();
|
|
27
29
|
return {
|
|
28
|
-
calls
|
|
29
|
-
...(
|
|
30
|
+
calls,
|
|
31
|
+
...(invocationFile ? { invocationFile } : {}),
|
|
32
|
+
...(collector ? { invocationWindowProof: collector.windowProof() } : {})
|
|
30
33
|
};
|
|
31
34
|
},
|
|
32
35
|
parseFinancialFile: readCodexFinancialFileForRegistry
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LocalAgentCall, LocalAgentLogDiagnostic, LocalAgentSourceScan } from "../localAgentLogs.js";
|
|
2
|
-
import type { ParsedInvocationFile } from "../toolInvocations.js";
|
|
2
|
+
import type { ParsedInvocationFile, ParsedInvocationWindowProof } from "../toolInvocations.js";
|
|
3
3
|
/**
|
|
4
4
|
* Public format identity contract. Add future parser IDs here as part of the
|
|
5
5
|
* registry-owned change so existing exhaustive consumers do not see an
|
|
@@ -78,6 +78,7 @@ export type LocalAgentFormatParseContext = {
|
|
|
78
78
|
export type LocalAgentFormatParseResult = {
|
|
79
79
|
calls: LocalAgentCall[];
|
|
80
80
|
invocationFile?: ParsedInvocationFile;
|
|
81
|
+
invocationWindowProof?: ParsedInvocationWindowProof;
|
|
81
82
|
};
|
|
82
83
|
export type LocalAgentFormatFinancialFileContext = {
|
|
83
84
|
filePath: string;
|