dsh-fast 0.1.1

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +201 -0
  3. package/README.es.md +166 -0
  4. package/README.hi.md +166 -0
  5. package/README.md +166 -0
  6. package/README.pt.md +166 -0
  7. package/README.zh.md +166 -0
  8. package/THIRD_PARTY_NOTICES.md +20 -0
  9. package/cordis.patch.yml +55 -0
  10. package/lib/index.js +844 -0
  11. package/lib/types/analyze.d.ts +41 -0
  12. package/lib/types/analyze.d.ts.map +1 -0
  13. package/lib/types/analyze.js +121 -0
  14. package/lib/types/analyze.js.map +1 -0
  15. package/lib/types/collector.d.ts +82 -0
  16. package/lib/types/collector.d.ts.map +1 -0
  17. package/lib/types/collector.js +236 -0
  18. package/lib/types/collector.js.map +1 -0
  19. package/lib/types/config.d.ts +76 -0
  20. package/lib/types/config.d.ts.map +1 -0
  21. package/lib/types/config.js +93 -0
  22. package/lib/types/config.js.map +1 -0
  23. package/lib/types/estimate.d.ts +24 -0
  24. package/lib/types/estimate.d.ts.map +1 -0
  25. package/lib/types/estimate.js +37 -0
  26. package/lib/types/estimate.js.map +1 -0
  27. package/lib/types/index.d.ts +35 -0
  28. package/lib/types/index.d.ts.map +1 -0
  29. package/lib/types/index.js +201 -0
  30. package/lib/types/index.js.map +1 -0
  31. package/lib/types/model.d.ts +91 -0
  32. package/lib/types/model.d.ts.map +1 -0
  33. package/lib/types/model.js +11 -0
  34. package/lib/types/model.js.map +1 -0
  35. package/lib/types/sanitize.d.ts +33 -0
  36. package/lib/types/sanitize.d.ts.map +1 -0
  37. package/lib/types/sanitize.js +59 -0
  38. package/lib/types/sanitize.js.map +1 -0
  39. package/lib/types/store.d.ts +74 -0
  40. package/lib/types/store.d.ts.map +1 -0
  41. package/lib/types/store.js +84 -0
  42. package/lib/types/store.js.map +1 -0
  43. package/lib/types/version.d.ts +3 -0
  44. package/lib/types/version.d.ts.map +1 -0
  45. package/lib/types/version.js +3 -0
  46. package/lib/types/version.js.map +1 -0
  47. package/package.json +147 -0
  48. package/src/analyze.ts +148 -0
  49. package/src/collector.ts +289 -0
  50. package/src/config.ts +163 -0
  51. package/src/estimate.ts +41 -0
  52. package/src/index.ts +235 -0
  53. package/src/model.ts +98 -0
  54. package/src/sanitize.ts +60 -0
  55. package/src/store.ts +100 -0
  56. package/src/version.ts +2 -0
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Pure report assembly: threshold-driven suggestions, the human-readable
3
+ * `/fast` text, and the final {@link FastReport}. Every function is pure of its
4
+ * inputs (no I/O, clock, random, or live-service access), so it is trivially
5
+ * unit-tested and safe to replay.
6
+ * @module dsh-fast/analyze
7
+ */
8
+ import type { FastReport, FastSnapshot } from './model.ts';
9
+ import type { ResolvedConfig } from './config.ts';
10
+ /** Report metadata supplied by the caller (identity, optional cwd, time). */
11
+ export interface ReportMeta {
12
+ sessionId: string;
13
+ cwd?: string;
14
+ generatedAt: number;
15
+ }
16
+ /**
17
+ * Build the optimization suggestions for a snapshot against the config
18
+ * thresholds. Suggestions are plain strings, one per distinct finding.
19
+ * @param snapshot - the metric snapshot.
20
+ * @param config - the resolved config (thresholds).
21
+ * @returns the ordered suggestions (may be empty).
22
+ */
23
+ export declare function buildSuggestions(snapshot: FastSnapshot, config: ResolvedConfig): string[];
24
+ /**
25
+ * Render a report as the human-readable `/fast` body.
26
+ * @param report - the assembled report.
27
+ * @returns the report text.
28
+ */
29
+ export declare function renderFastText(report: FastReport): string;
30
+ /**
31
+ * Assemble the final report from a snapshot plus caller metadata. Suggestions
32
+ * are computed here and the identity/cwd fields are sanitized before any model
33
+ * or durable surface sees them.
34
+ * @param snapshot - the metric snapshot.
35
+ * @param meta - session identity, optional cwd, and generation time.
36
+ * @param config - the resolved config (thresholds).
37
+ * @param version - the plugin version.
38
+ * @returns the complete report.
39
+ */
40
+ export declare function buildReport(snapshot: FastSnapshot, meta: ReportMeta, config: ResolvedConfig, version: string): FastReport;
41
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/analyze.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,EAAE,CA4CzF;AAYD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAsCzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAUzH"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Pure report assembly: threshold-driven suggestions, the human-readable
3
+ * `/fast` text, and the final {@link FastReport}. Every function is pure of its
4
+ * inputs (no I/O, clock, random, or live-service access), so it is trivially
5
+ * unit-tested and safe to replay.
6
+ * @module dsh-fast/analyze
7
+ */
8
+ import { sanitizePath, sanitizeText } from "./sanitize.js";
9
+ /**
10
+ * Build the optimization suggestions for a snapshot against the config
11
+ * thresholds. Suggestions are plain strings, one per distinct finding.
12
+ * @param snapshot - the metric snapshot.
13
+ * @param config - the resolved config (thresholds).
14
+ * @returns the ordered suggestions (may be empty).
15
+ */
16
+ export function buildSuggestions(snapshot, config) {
17
+ const suggestions = [];
18
+ const t = config.thresholds;
19
+ const { context, compaction, cache, spill } = snapshot;
20
+ if (context.systemTokens > t.systemPromptTokens) {
21
+ suggestions.push(`System prompt is large (${context.systemTokens} tokens, threshold ${t.systemPromptTokens}); consider trimming AGENTS.md, the skill directory, or persona.`);
22
+ }
23
+ if (context.toolSchemaTokens > t.toolSchemaTokens) {
24
+ suggestions.push(`Tool schema is large (${context.toolSchemaTokens} tokens, threshold ${t.toolSchemaTokens}); consider mounting fewer tools or tightening parameter descriptions.`);
25
+ }
26
+ if (context.surfaceTokens > t.surfaceTokens) {
27
+ suggestions.push(`Session surface is large (${context.surfaceTokens} tokens, threshold ${t.surfaceTokens}); consider /compact or clearing old tool results.`);
28
+ }
29
+ if (compaction.count >= t.compactionCountWarn) {
30
+ suggestions.push(`This session has triggered ${compaction.count} compactions; context pressure is high — compact earlier or raise the compaction threshold.`);
31
+ }
32
+ if (compaction.count > 0) {
33
+ const average = compaction.shadowedTokens / compaction.count;
34
+ if (average > t.compactionShadowTokens) {
35
+ suggestions.push(`Average compaction shadows ${Math.round(average)} tokens (threshold ${t.compactionShadowTokens}); the compaction threshold may be too conservative.`);
36
+ }
37
+ }
38
+ if (cache.hitRate !== null && cache.hitRate < t.cacheHitRateFloor) {
39
+ suggestions.push(`LLM cache hit rate is only ${Math.round(cache.hitRate * 100)}% (threshold ${Math.round(t.cacheHitRateFloor * 100)}%); consider enabling or tuning prompt caching.`);
40
+ }
41
+ if (spill.detectedSpilledResults === 0 && context.surfaceTokens > t.surfaceTokens / 2) {
42
+ suggestions.push('Surface volume is high with no spill hits; if spill-policy is not enabled, consider enabling it to protect the context window.');
43
+ }
44
+ return suggestions;
45
+ }
46
+ /** Format one nullable millisecond duration. */
47
+ function formatMs(value) {
48
+ return value === null ? 'n/a' : `${value} ms`;
49
+ }
50
+ /** Format one nullable ratio as a percentage. */
51
+ function formatPercent(value) {
52
+ return value === null ? 'n/a' : `${Math.round(value * 100)}%`;
53
+ }
54
+ /**
55
+ * Render a report as the human-readable `/fast` body.
56
+ * @param report - the assembled report.
57
+ * @returns the report text.
58
+ */
59
+ export function renderFastText(report) {
60
+ const { load, spill, compaction, context, cache } = report;
61
+ const lines = [
62
+ `dsh-fast ${report.version} — performance report`,
63
+ `session: ${report.sessionId}`,
64
+ '',
65
+ '## Session load',
66
+ `kind: ${load.kind}`,
67
+ `seed events: ${load.seedEvents}`,
68
+ `time to first request: ${formatMs(load.timeToFirstRequestMs)}`,
69
+ '',
70
+ '## Spill',
71
+ `detected spilled results: ${spill.detectedSpilledResults} (heuristic)`,
72
+ '',
73
+ '## Compaction',
74
+ `count: ${compaction.count} (manual: ${compaction.manual}, automatic: ${compaction.automatic})`,
75
+ `shadowed tokens: ${compaction.shadowedTokens}`,
76
+ '',
77
+ '## Context volume',
78
+ `total: ${context.totalTokens} tokens`,
79
+ `system (AGENTS.md/skills/persona): ${context.systemTokens} (${formatPercent(context.systemShare)})`,
80
+ `tool schema: ${context.toolSchemaTokens} (${formatPercent(context.toolsShare)})`,
81
+ `surface: ${context.surfaceTokens} (${formatPercent(context.surfaceShare)})`,
82
+ '',
83
+ '## LLM cache',
84
+ `input: ${cache.inputTokens}, cache read: ${cache.cacheReadTokens}, cache write: ${cache.cacheWriteTokens}, output: ${cache.outputTokens}`,
85
+ `hit rate: ${formatPercent(cache.hitRate)}`,
86
+ ];
87
+ if (report.cwd !== undefined) {
88
+ lines.splice(lines.length, 0, `cwd: ${report.cwd}`);
89
+ }
90
+ lines.push('', '## Suggestions');
91
+ if (report.suggestions.length === 0) {
92
+ lines.push('- none');
93
+ }
94
+ else {
95
+ for (const suggestion of report.suggestions)
96
+ lines.push(`- ${suggestion}`);
97
+ }
98
+ return lines.join('\n');
99
+ }
100
+ /**
101
+ * Assemble the final report from a snapshot plus caller metadata. Suggestions
102
+ * are computed here and the identity/cwd fields are sanitized before any model
103
+ * or durable surface sees them.
104
+ * @param snapshot - the metric snapshot.
105
+ * @param meta - session identity, optional cwd, and generation time.
106
+ * @param config - the resolved config (thresholds).
107
+ * @param version - the plugin version.
108
+ * @returns the complete report.
109
+ */
110
+ export function buildReport(snapshot, meta, config, version) {
111
+ return {
112
+ ...snapshot,
113
+ generator: 'dsh-fast',
114
+ version,
115
+ sessionId: sanitizeText(meta.sessionId, 128),
116
+ generatedAt: meta.generatedAt,
117
+ suggestions: buildSuggestions(snapshot, config),
118
+ ...(meta.cwd === undefined ? {} : { cwd: sanitizePath(meta.cwd, 256) }),
119
+ };
120
+ }
121
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The session/event collector: folds the durable session log into per-session
3
+ * counters and builds {@link FastSnapshot}s. All inline work is O(1) per event
4
+ * (increment counters, record the latest header) — the expensive context
5
+ * measurement is deferred to {@link snapshot}, which runs on the async
6
+ * sampling timer or on demand, never in the append hot path. State lives in a
7
+ * `Map` keyed by the live Session so the timer can iterate it.
8
+ * @module dsh-fast/collector
9
+ */
10
+ import type { Session, SessionEvent, ToolResultMessage } from '@deepseek-ai/dsh-session';
11
+ import type { FastSnapshot, ContextStats } from './model.ts';
12
+ import type { ResolvedConfig } from './config.ts';
13
+ /**
14
+ * The structural surface of the optional `ctx.tokenMeter` service. Only the
15
+ * fields dsh-fast reads are declared; the service is optional, so a host
16
+ * without it still reports system/tool-schema volumes (surface/total fall back
17
+ * to the header heuristic).
18
+ */
19
+ export interface TokenMeasurement {
20
+ readonly totalTokens: number;
21
+ readonly surfaceTokens: number;
22
+ }
23
+ /** Lazy lookup of the optional token meter. */
24
+ export type MeasureFn = (session: Session) => TokenMeasurement | undefined;
25
+ /** Flatten a tool result's model-facing text blocks to one string. */
26
+ export declare function flattenToolResultText(message: ToolResultMessage): string;
27
+ /**
28
+ * Best-effort spill detection: a spilled tool result is one whose durable text
29
+ * carries the spill-policy notice (`Full … stored at: <locator>`). No dedicated
30
+ * session event exists, so this is a documented heuristic, not a hard signal.
31
+ * @param message - the tool result message.
32
+ * @returns true when the result looks spilled.
33
+ */
34
+ export declare function detectSpilledResult(message: ToolResultMessage): boolean;
35
+ /** Fraction of `total` each bucket represents (0 when the total is 0). */
36
+ export declare function sharesOf(total: number, system: number, tools: number, surface: number): Pick<ContextStats, 'systemShare' | 'toolsShare' | 'surfaceShare'>;
37
+ /** Cache hit rate from aggregate tokens: `cacheRead / (input + cacheRead)`. */
38
+ export declare function hitRateOf(input: number, cacheRead: number): number | null;
39
+ /**
40
+ * The event → snapshot collector over real Sessions. State is adopted lazily on
41
+ * the first event so an HMR reload (which does not replay `session/created`)
42
+ * still adopts existing live sessions.
43
+ */
44
+ export declare class FastCollector {
45
+ private readonly config;
46
+ private readonly live;
47
+ /** @param config - the resolved plugin config. */
48
+ constructor(config: ResolvedConfig);
49
+ /** Adopt a session at its creation announcement. */
50
+ handleSessionCreated(session: Session): void;
51
+ /** Drop a session leaving the store. */
52
+ handleSessionDisposed(session: Session): void;
53
+ /**
54
+ * Fold one appended session event (O(1) per event).
55
+ * @param session - the session the event belongs to.
56
+ * @param event - the appended event.
57
+ */
58
+ handleEvent(session: Session, event: SessionEvent): void;
59
+ /** The live sessions the sampling timer iterates. */
60
+ liveSessions(): IterableIterator<Session>;
61
+ /** Whether a session is still live (adopted and not disposed). */
62
+ has(session: Session): boolean;
63
+ /** Whether a session has un-persisted changes. */
64
+ isDirty(session: Session): boolean;
65
+ /** Clear the dirty flag after a snapshot is appended. */
66
+ markClean(session: Session): void;
67
+ /**
68
+ * Build the current metric snapshot for one session. This is the only place
69
+ * the optional token meter is consulted, so it never runs in the append path.
70
+ * @param session - the session to snapshot.
71
+ * @param measure - optional token-meter measure function.
72
+ * @returns the snapshot.
73
+ */
74
+ snapshot(session: Session, measure?: MeasureFn): FastSnapshot;
75
+ /** Aggregate cache counters into the report shape. */
76
+ private cacheStats;
77
+ /** Fold one provider usage record. */
78
+ private foldUsage;
79
+ /** Adopt (or return the existing) live state for one session. */
80
+ private adopt;
81
+ }
82
+ //# sourceMappingURL=collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/collector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAGxF,OAAO,KAAK,EAAE,YAAY,EAAc,YAAY,EAAE,MAAM,YAAY,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAED,+CAA+C;AAC/C,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,gBAAgB,GAAG,SAAS,CAAA;AAK1E,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAQxE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAGvE;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,IAAI,CAAC,YAAY,EAAE,aAAa,GAAG,YAAY,GAAG,cAAc,CAAC,CAOnE;AAED,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIzE;AAsBD;;;;GAIG;AACH,qBAAa,aAAa;IAIZ,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IAErD,kDAAkD;gBACrB,MAAM,EAAE,cAAc;IAEnD,oDAAoD;IACpD,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI5C,wCAAwC;IACxC,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI7C;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;IAmCxD,qDAAqD;IACrD,YAAY,IAAI,gBAAgB,CAAC,OAAO,CAAC;IAIzC,kEAAkE;IAClE,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAI9B,kDAAkD;IAClD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAIlC,yDAAyD;IACzD,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAKjC;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,GAAG,YAAY;IAgC7D,sDAAsD;IACtD,OAAO,CAAC,UAAU;IAUlB,sCAAsC;IACtC,OAAO,CAAC,SAAS;IAYjB,iEAAiE;IACjE,OAAO,CAAC,KAAK;CAwBd"}
@@ -0,0 +1,236 @@
1
+ /**
2
+ * The session/event collector: folds the durable session log into per-session
3
+ * counters and builds {@link FastSnapshot}s. All inline work is O(1) per event
4
+ * (increment counters, record the latest header) — the expensive context
5
+ * measurement is deferred to {@link snapshot}, which runs on the async
6
+ * sampling timer or on demand, never in the append hot path. State lives in a
7
+ * `Map` keyed by the live Session so the timer can iterate it.
8
+ * @module dsh-fast/collector
9
+ */
10
+ import { estimateSystemTokens, estimateToolsTokens } from "./estimate.js";
11
+ /** The durable marker every spill notice carries (`... Full ... stored at: <locator> ...`). */
12
+ const SPILL_NOTICE_MARKERS = ['Full', 'stored at:'];
13
+ /** Flatten a tool result's model-facing text blocks to one string. */
14
+ export function flattenToolResultText(message) {
15
+ const block = message.content[0];
16
+ if (block === undefined)
17
+ return '';
18
+ let text = '';
19
+ for (const inner of block.content) {
20
+ if (inner.type === 'text')
21
+ text += inner.text;
22
+ }
23
+ return text;
24
+ }
25
+ /**
26
+ * Best-effort spill detection: a spilled tool result is one whose durable text
27
+ * carries the spill-policy notice (`Full … stored at: <locator>`). No dedicated
28
+ * session event exists, so this is a documented heuristic, not a hard signal.
29
+ * @param message - the tool result message.
30
+ * @returns true when the result looks spilled.
31
+ */
32
+ export function detectSpilledResult(message) {
33
+ const text = flattenToolResultText(message);
34
+ return SPILL_NOTICE_MARKERS.every(marker => text.includes(marker));
35
+ }
36
+ /** Fraction of `total` each bucket represents (0 when the total is 0). */
37
+ export function sharesOf(total, system, tools, surface) {
38
+ if (total <= 0)
39
+ return { systemShare: 0, toolsShare: 0, surfaceShare: 0 };
40
+ return {
41
+ systemShare: system / total,
42
+ toolsShare: tools / total,
43
+ surfaceShare: surface / total,
44
+ };
45
+ }
46
+ /** Cache hit rate from aggregate tokens: `cacheRead / (input + cacheRead)`. */
47
+ export function hitRateOf(input, cacheRead) {
48
+ const denominator = input + cacheRead;
49
+ if (denominator <= 0)
50
+ return null;
51
+ return cacheRead / denominator;
52
+ }
53
+ /**
54
+ * The event → snapshot collector over real Sessions. State is adopted lazily on
55
+ * the first event so an HMR reload (which does not replay `session/created`)
56
+ * still adopts existing live sessions.
57
+ */
58
+ export class FastCollector {
59
+ config;
60
+ live = new Map();
61
+ /** @param config - the resolved plugin config. */
62
+ constructor(config) {
63
+ this.config = config;
64
+ }
65
+ /** Adopt a session at its creation announcement. */
66
+ handleSessionCreated(session) {
67
+ this.adopt(session);
68
+ }
69
+ /** Drop a session leaving the store. */
70
+ handleSessionDisposed(session) {
71
+ this.live.delete(session);
72
+ }
73
+ /**
74
+ * Fold one appended session event (O(1) per event).
75
+ * @param session - the session the event belongs to.
76
+ * @param event - the appended event.
77
+ */
78
+ handleEvent(session, event) {
79
+ const state = this.adopt(session);
80
+ switch (event.type) {
81
+ case 'request/header':
82
+ state.lastHeader = event.data.header;
83
+ if (state.timeToFirstRequestMs === null) {
84
+ state.timeToFirstRequestMs = Math.max(0, event.time - state.createdAtMs);
85
+ state.dirty = true;
86
+ }
87
+ break;
88
+ case 'assistant/message':
89
+ this.foldUsage(state, event.data.usage);
90
+ break;
91
+ case 'compaction/start':
92
+ state.compactionCount += 1;
93
+ if (event.data.sourceCommandId === undefined)
94
+ state.compactionAutomatic += 1;
95
+ else
96
+ state.compactionManual += 1;
97
+ state.dirty = true;
98
+ break;
99
+ case 'compaction/summary':
100
+ state.compactionShadowedTokens += event.data.shadowedTokenCount;
101
+ state.dirty = true;
102
+ break;
103
+ case 'tool/result':
104
+ if (this.config.detectSpilledResults && detectSpilledResult(event.data.message)) {
105
+ state.spilledResults += 1;
106
+ state.dirty = true;
107
+ }
108
+ break;
109
+ default:
110
+ // Unknown or plugin-owned session events: nothing to fold.
111
+ break;
112
+ }
113
+ }
114
+ /** The live sessions the sampling timer iterates. */
115
+ liveSessions() {
116
+ return this.live.keys();
117
+ }
118
+ /** Whether a session is still live (adopted and not disposed). */
119
+ has(session) {
120
+ return this.live.has(session);
121
+ }
122
+ /** Whether a session has un-persisted changes. */
123
+ isDirty(session) {
124
+ return this.live.get(session)?.dirty ?? false;
125
+ }
126
+ /** Clear the dirty flag after a snapshot is appended. */
127
+ markClean(session) {
128
+ const state = this.live.get(session);
129
+ if (state !== undefined)
130
+ state.dirty = false;
131
+ }
132
+ /**
133
+ * Build the current metric snapshot for one session. This is the only place
134
+ * the optional token meter is consulted, so it never runs in the append path.
135
+ * @param session - the session to snapshot.
136
+ * @param measure - optional token-meter measure function.
137
+ * @returns the snapshot.
138
+ */
139
+ snapshot(session, measure) {
140
+ const state = this.live.get(session);
141
+ if (state === undefined)
142
+ return emptySnapshot();
143
+ const measurement = measure === undefined ? undefined : measure(session);
144
+ const systemTokens = estimateSystemTokens(state.lastHeader);
145
+ const toolSchemaTokens = estimateToolsTokens(state.lastHeader);
146
+ const surfaceTokens = measurement?.surfaceTokens ?? 0;
147
+ const totalTokens = measurement?.totalTokens ?? (systemTokens + toolSchemaTokens + surfaceTokens);
148
+ return {
149
+ load: {
150
+ kind: state.kind,
151
+ seedEvents: state.firstLiveSeq,
152
+ timeToFirstRequestMs: state.timeToFirstRequestMs,
153
+ },
154
+ spill: { detectedSpilledResults: state.spilledResults, heuristic: true },
155
+ compaction: {
156
+ count: state.compactionCount,
157
+ manual: state.compactionManual,
158
+ automatic: state.compactionAutomatic,
159
+ shadowedTokens: state.compactionShadowedTokens,
160
+ },
161
+ context: {
162
+ totalTokens,
163
+ systemTokens,
164
+ toolSchemaTokens,
165
+ surfaceTokens,
166
+ ...sharesOf(totalTokens, systemTokens, toolSchemaTokens, surfaceTokens),
167
+ },
168
+ cache: this.cacheStats(state),
169
+ };
170
+ }
171
+ /** Aggregate cache counters into the report shape. */
172
+ cacheStats(state) {
173
+ return {
174
+ inputTokens: state.inputTokens,
175
+ cacheReadTokens: state.cacheReadTokens,
176
+ cacheWriteTokens: state.cacheWriteTokens,
177
+ outputTokens: state.outputTokens,
178
+ hitRate: hitRateOf(state.inputTokens, state.cacheReadTokens),
179
+ };
180
+ }
181
+ /** Fold one provider usage record. */
182
+ foldUsage(state, usage) {
183
+ if (usage === undefined)
184
+ return;
185
+ state.inputTokens += usage.inputTokens;
186
+ state.outputTokens += usage.outputTokens;
187
+ state.cacheReadTokens += usage.cacheReadTokens ?? 0;
188
+ state.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
189
+ state.dirty = true;
190
+ }
191
+ /** Adopt (or return the existing) live state for one session. */
192
+ adopt(session) {
193
+ const existing = this.live.get(session);
194
+ if (existing !== undefined)
195
+ return existing;
196
+ const state = {
197
+ session,
198
+ createdAtMs: Date.now(),
199
+ kind: session.firstLiveSeq > 0 ? 'restore' : 'open',
200
+ firstLiveSeq: session.firstLiveSeq,
201
+ timeToFirstRequestMs: null,
202
+ spilledResults: 0,
203
+ compactionCount: 0,
204
+ compactionManual: 0,
205
+ compactionAutomatic: 0,
206
+ compactionShadowedTokens: 0,
207
+ inputTokens: 0,
208
+ cacheReadTokens: 0,
209
+ cacheWriteTokens: 0,
210
+ outputTokens: 0,
211
+ lastHeader: undefined,
212
+ dirty: true,
213
+ };
214
+ this.live.set(session, state);
215
+ return state;
216
+ }
217
+ }
218
+ /** A zeroed snapshot for a session that is no longer live. */
219
+ function emptySnapshot() {
220
+ return {
221
+ load: { kind: 'open', seedEvents: 0, timeToFirstRequestMs: null },
222
+ spill: { detectedSpilledResults: 0, heuristic: true },
223
+ compaction: { count: 0, manual: 0, automatic: 0, shadowedTokens: 0 },
224
+ context: {
225
+ totalTokens: 0,
226
+ systemTokens: 0,
227
+ toolSchemaTokens: 0,
228
+ surfaceTokens: 0,
229
+ systemShare: 0,
230
+ toolsShare: 0,
231
+ surfaceShare: 0,
232
+ },
233
+ cache: { inputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, outputTokens: 0, hitRate: null },
234
+ };
235
+ }
236
+ //# sourceMappingURL=collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/collector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Config schema and resolution for `dsh-fast`. Every tunable is a validated
3
+ * {@link Config} field changeable from cordis.yml; the resolution step
4
+ * validates numeric bounds so misconfiguration fails loud at mount. The plugin
5
+ * is read-only and safe, so it defaults to enabled — but `enabled: false`
6
+ * mounts nothing.
7
+ * @module dsh-fast/config
8
+ */
9
+ import z from '@deepseek-ai/schemastery';
10
+ /** Privacy switches for the report surfaces. */
11
+ export interface PrivacyConfig {
12
+ /** Include the sanitized session working directory in `/fast` and `fast_report`. Off by default: a local path is sensitive. */
13
+ includeCwd?: boolean;
14
+ }
15
+ /** Async sampling policy (off the model path). */
16
+ export interface SamplingConfig {
17
+ /** How often active sessions are sampled, in milliseconds. */
18
+ snapshotIntervalMs?: number;
19
+ /** How many samples to retain per session in the durable domain history. */
20
+ maxHistorySamples?: number;
21
+ }
22
+ /** Suggestion thresholds — the values that decide which optimization notes appear. */
23
+ export interface ThresholdConfig {
24
+ /** Warn when assembled system-prompt tokens (AGENTS.md + skills + persona) exceed this. */
25
+ systemPromptTokens?: number;
26
+ /** Warn when tool-schema tokens exceed this. */
27
+ toolSchemaTokens?: number;
28
+ /** Warn when conversation-surface tokens exceed this. */
29
+ surfaceTokens?: number;
30
+ /** Warn when the LLM cache hit rate falls below this (0..1). */
31
+ cacheHitRateFloor?: number;
32
+ /** Warn once the session has triggered this many compactions. */
33
+ compactionCountWarn?: number;
34
+ /** Warn when the average shadowed token count per completed summary exceeds this. */
35
+ compactionShadowTokens?: number;
36
+ }
37
+ /** Spill-detection switch. */
38
+ export interface SpillConfig {
39
+ /** Detect spilled tool results from the durable spill-notice marker. Best-effort; see README "Known limitations". */
40
+ detectSpilledResults?: boolean;
41
+ }
42
+ /** Raw plugin config — every field optional; {@link resolveConfig} supplies the defaults. */
43
+ export interface Config {
44
+ /** Master switch. Off by default? No — diagnostics are read-only and safe, so on by default. */
45
+ enabled?: boolean;
46
+ privacy?: PrivacyConfig;
47
+ sampling?: SamplingConfig;
48
+ thresholds?: ThresholdConfig;
49
+ spill?: SpillConfig;
50
+ }
51
+ /** Fully resolved config handed to the runtime. */
52
+ export interface ResolvedConfig {
53
+ readonly enabled: boolean;
54
+ readonly includeCwd: boolean;
55
+ readonly snapshotIntervalMs: number;
56
+ readonly maxHistorySamples: number;
57
+ readonly detectSpilledResults: boolean;
58
+ readonly thresholds: {
59
+ readonly systemPromptTokens: number;
60
+ readonly toolSchemaTokens: number;
61
+ readonly surfaceTokens: number;
62
+ readonly cacheHitRateFloor: number;
63
+ readonly compactionCountWarn: number;
64
+ readonly compactionShadowTokens: number;
65
+ };
66
+ }
67
+ /** Schemastery schema: the loader validates and fills defaults before `apply`. */
68
+ export declare const Config: z<Config>;
69
+ /**
70
+ * Validate raw values and fill explicit defaults. Invalid bounds throw here —
71
+ * misconfiguration fails loud at mount even without the Schemastery loader.
72
+ * @param config - raw (possibly partial) plugin config.
73
+ * @returns the fully resolved config.
74
+ */
75
+ export declare function resolveConfig(config?: Config): ResolvedConfig;
76
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,+HAA+H;IAC/H,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,kDAAkD;AAClD,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,qFAAqF;IACrF,sBAAsB,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,8BAA8B;AAC9B,MAAM,WAAW,WAAW;IAC1B,qHAAqH;IACrH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,6FAA6F;AAC7F,MAAM,WAAW,MAAM;IACrB,gGAAgG;IAChG,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,KAAK,CAAC,EAAE,WAAW,CAAA;CACpB;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAA;IACnC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAA;IACtC,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAA;QACnC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;QACjC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;QAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;QAClC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;QACpC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAA;KACxC,CAAA;CACF;AAED,kFAAkF;AAClF,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CA2B3B,CAAA;AAgBF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,GAAE,MAAW,GAAG,cAAc,CAsCjE"}