forkit-ai-footprints 0.2.0 → 0.2.4

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 (120) hide show
  1. package/PRIVACY.md +57 -27
  2. package/README.md +43 -41
  3. package/STATUS.md +92 -37
  4. package/dist/activity-page.d.ts +13 -0
  5. package/dist/activity-page.js +105 -0
  6. package/dist/activity-view-model.d.ts +154 -0
  7. package/dist/activity-view-model.js +364 -0
  8. package/dist/agents.d.ts +2 -0
  9. package/dist/agents.js +108 -6
  10. package/dist/bootstrap/.metadata_never_index +0 -0
  11. package/dist/bootstrap/Forkit AI Footprint.app.zip +0 -0
  12. package/dist/cli.js +91 -14
  13. package/dist/device-presence.d.ts +17 -0
  14. package/dist/device-presence.js +64 -0
  15. package/dist/global-preview.d.ts +34 -0
  16. package/dist/global-preview.js +209 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +5 -1
  19. package/dist/insights.d.ts +1 -1
  20. package/dist/insights.js +10 -8
  21. package/dist/local-observation-state.d.ts +43 -0
  22. package/dist/local-observation-state.js +124 -0
  23. package/dist/locales.json +564 -108
  24. package/dist/localization.d.ts +564 -108
  25. package/dist/localization.js +40 -30
  26. package/dist/monitor.d.ts +38 -2
  27. package/dist/monitor.js +257 -19
  28. package/dist/runtime-activity.d.ts +4 -1
  29. package/dist/runtime-activity.js +21 -5
  30. package/dist/server.d.ts +5 -0
  31. package/dist/server.js +160 -17
  32. package/dist/share-page.d.ts +2 -1
  33. package/dist/share-page.js +16 -52
  34. package/dist/sharing.d.ts +12 -1
  35. package/dist/sharing.js +59 -1
  36. package/dist/types.d.ts +1 -1
  37. package/dist/workflow-context.d.ts +13 -0
  38. package/dist/workflow-context.js +144 -0
  39. package/docs/GLOBAL_DISTRIBUTION.md +7 -3
  40. package/docs/MACOS_ACCURACY_GATE.md +3 -3
  41. package/docs/MACOS_TESTER_GUIDE.md +11 -10
  42. package/package.json +6 -6
  43. package/release/channels.json +2 -2
  44. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Info.plist +0 -5
  45. package/dist/bootstrap/Forkit AI Footprint.app/Contents/MacOS/Forkit AI Footprint +0 -0
  46. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/ForkitAIFootprint.icns +0 -0
  47. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/ForkitStatusTemplate.png +0 -0
  48. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/agents.d.ts +0 -22
  49. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/agents.js +0 -281
  50. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/aggregate.d.ts +0 -28
  51. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/aggregate.js +0 -151
  52. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/assets/forkit-icon-dark.png +0 -0
  53. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/assets/forkit-icon-light.png +0 -0
  54. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/census.d.ts +0 -3
  55. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/census.js +0 -196
  56. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/cli.d.ts +0 -32
  57. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/cli.js +0 -404
  58. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/doctor.d.ts +0 -14
  59. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/doctor.js +0 -71
  60. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/endpoints.d.ts +0 -8
  61. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/endpoints.js +0 -45
  62. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/evaluate.d.ts +0 -44
  63. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/evaluate.js +0 -102
  64. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/filesystem.d.ts +0 -11
  65. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/filesystem.js +0 -226
  66. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/format.d.ts +0 -7
  67. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/format.js +0 -78
  68. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/hash.d.ts +0 -4
  69. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/hash.js +0 -16
  70. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/index.d.ts +0 -22
  71. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/index.js +0 -58
  72. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/insights.d.ts +0 -13
  73. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/insights.js +0 -76
  74. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/local-device.d.ts +0 -19
  75. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/local-device.js +0 -81
  76. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/locales.json +0 -272
  77. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/localization.d.ts +0 -277
  78. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/localization.js +0 -55
  79. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/mcp.d.ts +0 -9
  80. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/mcp.js +0 -93
  81. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/monitor.d.ts +0 -99
  82. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/monitor.js +0 -285
  83. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/base.d.ts +0 -34
  84. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/base.js +0 -88
  85. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/index.d.ts +0 -6
  86. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/index.js +0 -24
  87. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/lmstudio.d.ts +0 -9
  88. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/lmstudio.js +0 -71
  89. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/ollama.d.ts +0 -12
  90. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/ollama.js +0 -171
  91. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/openai-compatible.d.ts +0 -10
  92. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/providers/openai-compatible.js +0 -68
  93. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/resource-evidence.d.ts +0 -17
  94. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/resource-evidence.js +0 -52
  95. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-activity.d.ts +0 -5
  96. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-activity.js +0 -66
  97. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-identity.d.ts +0 -7
  98. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/runtime-identity.js +0 -66
  99. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/server.d.ts +0 -21
  100. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/server.js +0 -308
  101. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/share-page.d.ts +0 -75
  102. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/share-page.js +0 -167
  103. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/sharing.d.ts +0 -35
  104. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/sharing.js +0 -67
  105. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/tools.d.ts +0 -9
  106. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/tools.js +0 -106
  107. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/types.d.ts +0 -166
  108. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/types.js +0 -3
  109. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/version.d.ts +0 -4
  110. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/dist/version.js +0 -13
  111. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/index.d.ts +0 -76
  112. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/index.js +0 -366
  113. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/license +0 -9
  114. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/package.json +0 -44
  115. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/readme.md +0 -56
  116. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/vendor/fastlist-0.3.0-x64.exe +0 -0
  117. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/node_modules/ps-list/vendor/fastlist-0.3.0-x86.exe +0 -0
  118. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/app/package.json +0 -6
  119. package/dist/bootstrap/Forkit AI Footprint.app/Contents/Resources/runtime/node +0 -0
  120. package/dist/bootstrap/Forkit AI Footprint.app/Contents/_CodeSignature/CodeResources +0 -931
@@ -0,0 +1,364 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeActivitySegments = mergeActivitySegments;
4
+ exports.normalizeActivitySegments = normalizeActivitySegments;
5
+ exports.calculateSystemContributions = calculateSystemContributions;
6
+ exports.buildResourceHistoryView = buildResourceHistoryView;
7
+ exports.aggregateCurrentAiResources = aggregateCurrentAiResources;
8
+ exports.availableLocalDateKeys = availableLocalDateKeys;
9
+ exports.formatObservationDateRange = formatObservationDateRange;
10
+ exports.selectedTimeRange = selectedTimeRange;
11
+ exports.resolveSelectedSystemSignature = resolveSelectedSystemSignature;
12
+ exports.insightEvidenceTarget = insightEvidenceTarget;
13
+ exports.buildActivityExplorerViewModel = buildActivityExplorerViewModel;
14
+ function finiteTimestamp(value) {
15
+ const parsed = Date.parse(value);
16
+ return Number.isFinite(parsed) ? parsed : null;
17
+ }
18
+ function sameStrings(left, right) {
19
+ return left.length === right.length && left.every((value, index) => value === right[index]);
20
+ }
21
+ function canMerge(left, right, toleranceMs) {
22
+ return left.state === right.state
23
+ && left.valid === right.valid
24
+ && left.continuity_id === right.continuity_id
25
+ && left.excluded_reason === right.excluded_reason
26
+ && sameStrings(left.system_signatures, right.system_signatures)
27
+ && right.start_ms - left.end_ms <= toleranceMs;
28
+ }
29
+ function mergeActivitySegments(segments, toleranceMs) {
30
+ const merged = [];
31
+ for (const segment of segments) {
32
+ const previous = merged.at(-1);
33
+ if (previous && canMerge(previous, segment, toleranceMs))
34
+ previous.end_ms = Math.max(previous.end_ms, segment.end_ms);
35
+ else
36
+ merged.push({ ...segment, system_signatures: [...segment.system_signatures] });
37
+ }
38
+ return merged;
39
+ }
40
+ function normalizeActivitySegments(timeline, sampleIntervalMs) {
41
+ const safeInterval = Math.max(1, sampleIntervalMs);
42
+ const source = timeline.flatMap((segment) => {
43
+ const start = finiteTimestamp(segment.started_at);
44
+ const end = finiteTimestamp(segment.ended_at);
45
+ if (start === null || end === null || end < start)
46
+ return [];
47
+ return [{
48
+ start_ms: start,
49
+ end_ms: end,
50
+ state: segment.state === 'working-now' ? 'active' : 'idle',
51
+ valid: true,
52
+ system_signatures: [...new Set(segment.product_signatures)].sort(),
53
+ observation_id: segment.observation_id ?? null,
54
+ continuity_id: segment.continuity_id ?? segment.observation_id ?? null,
55
+ excluded_reason: null,
56
+ }];
57
+ }).sort((left, right) => left.start_ms - right.start_ms || left.end_ms - right.end_ms);
58
+ const withExcluded = [];
59
+ for (const segment of source) {
60
+ const previous = withExcluded.at(-1);
61
+ if (previous) {
62
+ const gap = segment.start_ms - previous.end_ms;
63
+ const sessionChanged = previous.observation_id !== null
64
+ && segment.observation_id !== null
65
+ && previous.observation_id !== segment.observation_id;
66
+ if (gap > safeInterval * 3 || (sessionChanged && gap > safeInterval * 1.5)) {
67
+ withExcluded.push({
68
+ start_ms: previous.end_ms,
69
+ end_ms: segment.start_ms,
70
+ state: 'excluded',
71
+ valid: false,
72
+ system_signatures: [],
73
+ observation_id: null,
74
+ continuity_id: null,
75
+ excluded_reason: null,
76
+ });
77
+ }
78
+ }
79
+ withExcluded.push(segment);
80
+ }
81
+ const merged = mergeActivitySegments(withExcluded, safeInterval * 1.5);
82
+ const activeBlocks = [];
83
+ merged.forEach((segment, index) => {
84
+ if (segment.state !== 'active')
85
+ return;
86
+ const previous = activeBlocks.at(-1);
87
+ const continues = previous
88
+ && previous.continuity_id === segment.continuity_id
89
+ && segment.start_ms - previous.end_ms <= safeInterval * 3
90
+ && merged.slice(previous.indexes.at(-1) + 1, index).every((between) => between.state === 'active');
91
+ if (continues) {
92
+ previous.end_ms = Math.max(previous.end_ms, segment.end_ms);
93
+ previous.indexes.push(index);
94
+ }
95
+ else
96
+ activeBlocks.push({ start_ms: segment.start_ms, end_ms: segment.end_ms, indexes: [index], continuity_id: segment.continuity_id });
97
+ });
98
+ const longestBlock = activeBlocks.sort((left, right) => (right.end_ms - right.start_ms) - (left.end_ms - left.start_ms))[0] ?? null;
99
+ const longestIndexes = new Set(longestBlock?.indexes ?? []);
100
+ const activeBlockByIndex = new Map(activeBlocks.flatMap((block, blockIndex) => block.indexes.map((index) => [index, blockIndex])));
101
+ return merged.map((segment, index) => ({
102
+ id: `segment-${index}-${segment.start_ms}`,
103
+ ...segment,
104
+ duration_seconds: Math.max(0, segment.end_ms - segment.start_ms) / 1000,
105
+ is_longest_active: longestIndexes.has(index),
106
+ active_block_id: activeBlockByIndex.get(index) ?? null,
107
+ }));
108
+ }
109
+ function calculateSystemContributions(products) {
110
+ const hasEmbeddedCodex = products.some((product) => product.signature === 'chatgpt-codex');
111
+ const active = products
112
+ .filter((product) => !(hasEmbeddedCodex && product.signature === 'chatgpt'))
113
+ .filter((product) => Number.isFinite(product.active_seconds) && product.active_seconds > 0)
114
+ .map((product) => ({
115
+ signature: product.signature,
116
+ name: product.name,
117
+ kind: product.kind,
118
+ active_seconds: Math.max(0, product.active_seconds),
119
+ }));
120
+ const total = active.reduce((sum, product) => sum + product.active_seconds, 0);
121
+ return active
122
+ .map((product) => ({ ...product, share: total > 0 ? product.active_seconds / total : 0 }))
123
+ .sort((left, right) => right.active_seconds - left.active_seconds || left.name.localeCompare(right.name));
124
+ }
125
+ function finiteNumber(value) {
126
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
127
+ }
128
+ function weightedAverage(values, value, weight) {
129
+ let total = 0;
130
+ let measured = 0;
131
+ for (const item of values) {
132
+ const next = value(item);
133
+ if (next === null)
134
+ continue;
135
+ const nextWeight = Math.max(0, weight(item));
136
+ if (nextWeight <= 0)
137
+ continue;
138
+ total += next * nextWeight;
139
+ measured += nextWeight;
140
+ }
141
+ return measured > 0 ? total / measured : null;
142
+ }
143
+ function rounded(value, digits = 1) {
144
+ const factor = 10 ** digits;
145
+ return Math.round(value * factor) / factor;
146
+ }
147
+ function buildResourceHistoryView(history = []) {
148
+ const buckets = history.flatMap((bucket) => {
149
+ const start = finiteTimestamp(bucket.started_at);
150
+ const end = finiteTimestamp(bucket.ended_at);
151
+ if (start === null || end === null || end < start)
152
+ return [];
153
+ return [{
154
+ start_ms: start,
155
+ end_ms: end,
156
+ system_signature: bucket.system_signature,
157
+ system_name: bucket.system_name,
158
+ kind: bucket.kind,
159
+ observed_seconds: Math.max(0, Number(bucket.observed_seconds) || 0),
160
+ active_seconds: Math.max(0, Number(bucket.active_seconds) || 0),
161
+ avg_cpu_percent: finiteNumber(bucket.avg_cpu_percent),
162
+ peak_cpu_percent: finiteNumber(bucket.peak_cpu_percent),
163
+ avg_memory_bytes: finiteNumber(bucket.avg_memory_bytes),
164
+ peak_memory_bytes: finiteNumber(bucket.peak_memory_bytes),
165
+ peak_process_count: Math.max(0, Math.trunc(Number(bucket.peak_process_count) || 0)),
166
+ sample_count: Math.max(0, Math.trunc(Number(bucket.sample_count) || 0)),
167
+ }];
168
+ }).sort((left, right) => left.start_ms - right.start_ms || left.system_name.localeCompare(right.system_name));
169
+ const bySystem = new Map();
170
+ for (const bucket of buckets)
171
+ bySystem.set(bucket.system_signature, [...(bySystem.get(bucket.system_signature) ?? []), bucket]);
172
+ const systems = [...bySystem.entries()].map(([signature, entries]) => ({
173
+ signature,
174
+ name: entries.at(-1)?.system_name ?? signature,
175
+ kind: entries.at(-1)?.kind ?? 'unknown',
176
+ observed_seconds: rounded(entries.reduce((sum, bucket) => sum + bucket.observed_seconds, 0)),
177
+ active_seconds: rounded(entries.reduce((sum, bucket) => sum + bucket.active_seconds, 0)),
178
+ avg_cpu_percent: (() => {
179
+ const value = weightedAverage(entries, (bucket) => bucket.avg_cpu_percent, (bucket) => bucket.observed_seconds);
180
+ return value === null ? null : rounded(value);
181
+ })(),
182
+ peak_cpu_percent: entries.reduce((maximum, bucket) => {
183
+ const value = finiteNumber(bucket.peak_cpu_percent);
184
+ return value === null ? maximum : maximum === null ? value : Math.max(maximum, value);
185
+ }, null),
186
+ avg_memory_bytes: (() => {
187
+ const value = weightedAverage(entries, (bucket) => bucket.avg_memory_bytes, (bucket) => bucket.observed_seconds);
188
+ return value === null ? null : Math.round(value);
189
+ })(),
190
+ peak_memory_bytes: entries.reduce((maximum, bucket) => {
191
+ const value = finiteNumber(bucket.peak_memory_bytes);
192
+ return value === null ? maximum : maximum === null ? value : Math.max(maximum, value);
193
+ }, null),
194
+ peak_process_count: entries.reduce((maximum, bucket) => Math.max(maximum, bucket.peak_process_count), 0),
195
+ sample_count: entries.reduce((sum, bucket) => sum + bucket.sample_count, 0),
196
+ })).sort((left, right) => right.active_seconds - left.active_seconds || right.observed_seconds - left.observed_seconds || left.name.localeCompare(right.name));
197
+ return { buckets, systems };
198
+ }
199
+ function aggregateCurrentAiResources(products) {
200
+ const cpu = products.map((product) => finiteNumber(product.cpu_percent)).filter((value) => value !== null);
201
+ const memory = products.map((product) => finiteNumber(product.memory_bytes)).filter((value) => value !== null);
202
+ return {
203
+ cpu_percent: cpu.length ? cpu.reduce((sum, value) => sum + Math.max(0, value), 0) : null,
204
+ memory_bytes: memory.length ? memory.reduce((sum, value) => sum + Math.max(0, value), 0) : null,
205
+ process_count: products.reduce((sum, product) => sum + Math.max(0, Math.trunc(product.process_count || 0)), 0),
206
+ contributing_system_count: products.filter((product) => finiteNumber(product.cpu_percent) !== null || finiteNumber(product.memory_bytes) !== null).length,
207
+ measurement: 'current-detected-process-trees',
208
+ };
209
+ }
210
+ function dateKeyFormatter(timeZone) {
211
+ return new Intl.DateTimeFormat('en-CA', {
212
+ timeZone,
213
+ year: 'numeric',
214
+ month: '2-digit',
215
+ day: '2-digit',
216
+ });
217
+ }
218
+ function dateKeyParts(timestampMs, formatter) {
219
+ const parts = Object.fromEntries(formatter.formatToParts(new Date(timestampMs)).map((part) => [part.type, part.value]));
220
+ return `${parts.year}-${parts.month}-${parts.day}`;
221
+ }
222
+ function availableLocalDateKeys(segments, timeZone) {
223
+ const keys = new Set();
224
+ const formatter = dateKeyFormatter(timeZone);
225
+ for (const segment of segments) {
226
+ if (segment.state === 'excluded')
227
+ continue;
228
+ const end = Math.max(segment.start_ms, segment.end_ms - 1);
229
+ for (let cursor = segment.start_ms; cursor < end; cursor += 6 * 60 * 60 * 1000)
230
+ keys.add(dateKeyParts(cursor, formatter));
231
+ keys.add(dateKeyParts(end, formatter));
232
+ }
233
+ return [...keys].sort();
234
+ }
235
+ function formatObservationDateRange(extent, locale, timeZone) {
236
+ if (!extent)
237
+ return null;
238
+ const keyFormatter = dateKeyFormatter(timeZone);
239
+ const startKey = dateKeyParts(extent.start_ms, keyFormatter);
240
+ const endKey = dateKeyParts(Math.max(extent.start_ms, extent.end_ms - 1), keyFormatter);
241
+ const formatter = new Intl.DateTimeFormat(locale, {
242
+ timeZone,
243
+ day: 'numeric',
244
+ month: 'short',
245
+ year: 'numeric',
246
+ });
247
+ const start = formatter.format(new Date(extent.start_ms));
248
+ if (startKey === endKey)
249
+ return start;
250
+ return `${start} – ${formatter.format(new Date(Math.max(extent.start_ms, extent.end_ms - 1)))}`;
251
+ }
252
+ function systemActivitySummaries(products, segments, contributions, sampleIntervalMs, resourceHistory) {
253
+ const contributionMap = new Map(contributions.map((item) => [item.signature, item]));
254
+ const resourceMap = new Map(resourceHistory.systems.map((item) => [item.signature, item]));
255
+ return products
256
+ .filter((product) => !(product.signature === 'chatgpt' && products.some((candidate) => candidate.signature === 'chatgpt-codex')))
257
+ .map((product) => {
258
+ const active = segments.filter((segment) => segment.state === 'active' && segment.system_signatures.includes(product.signature));
259
+ const periods = [];
260
+ for (const segment of active) {
261
+ const previous = periods.at(-1);
262
+ if (previous
263
+ && previous.observation_id === segment.observation_id
264
+ && segment.start_ms - previous.end_ms <= Math.max(1_500, sampleIntervalMs * 3)) {
265
+ previous.end_ms = Math.max(previous.end_ms, segment.end_ms);
266
+ }
267
+ else
268
+ periods.push({ start_ms: segment.start_ms, end_ms: segment.end_ms, observation_id: segment.observation_id });
269
+ }
270
+ const contribution = contributionMap.get(product.signature) ?? {
271
+ signature: product.signature,
272
+ name: product.name,
273
+ kind: product.kind,
274
+ active_seconds: Math.max(0, product.active_seconds || 0),
275
+ share: 0,
276
+ };
277
+ const resources = resourceMap.get(product.signature);
278
+ return {
279
+ ...contribution,
280
+ state: product.state,
281
+ period_count: periods.length,
282
+ longest_active_seconds: periods.reduce((maximum, period) => Math.max(maximum, (period.end_ms - period.start_ms) / 1000), 0),
283
+ first_observed_ms: periods[0]?.start_ms ?? null,
284
+ last_observed_ms: periods.at(-1)?.end_ms ?? null,
285
+ current_cpu_percent: finiteNumber(product.cpu_percent),
286
+ current_memory_bytes: finiteNumber(product.memory_bytes),
287
+ current_process_count: Math.max(0, Math.trunc(product.process_count || 0)),
288
+ resource_observed_seconds: resources?.observed_seconds ?? 0,
289
+ resource_active_seconds: resources?.active_seconds ?? 0,
290
+ avg_cpu_percent: resources?.avg_cpu_percent ?? null,
291
+ peak_cpu_percent: resources?.peak_cpu_percent ?? null,
292
+ avg_memory_bytes: resources?.avg_memory_bytes ?? null,
293
+ peak_memory_bytes: resources?.peak_memory_bytes ?? null,
294
+ peak_process_count: resources?.peak_process_count ?? 0,
295
+ resource_sample_count: resources?.sample_count ?? 0,
296
+ };
297
+ })
298
+ .sort((left, right) => right.active_seconds - left.active_seconds || left.name.localeCompare(right.name));
299
+ }
300
+ function selectedTimeRange(segment) {
301
+ return { start_ms: segment.start_ms, end_ms: segment.end_ms, duration_seconds: segment.duration_seconds };
302
+ }
303
+ function resolveSelectedSystemSignature(view, selectedSignature) {
304
+ return selectedSignature && view.systems.some((system) => system.signature === selectedSignature)
305
+ ? selectedSignature
306
+ : null;
307
+ }
308
+ function insightEvidenceTarget(kind, segments, contributions) {
309
+ if (kind === 'longest-block') {
310
+ const longest = segments.filter((segment) => segment.is_longest_active);
311
+ if (longest.length)
312
+ return { type: 'range', start_ms: longest[0].start_ms, end_ms: longest.at(-1).end_ms };
313
+ }
314
+ if (kind === 'workflow' || kind === 'hosted-local') {
315
+ const dominant = contributions[0];
316
+ if (dominant)
317
+ return { type: 'system', signature: dominant.signature };
318
+ }
319
+ return { type: 'observation' };
320
+ }
321
+ function buildActivityExplorerViewModel(snapshot, insights = []) {
322
+ const segments = normalizeActivitySegments(snapshot.timeline, snapshot.sample_interval_ms);
323
+ const resourceHistory = buildResourceHistoryView(snapshot.resource_history ?? []);
324
+ const contributions = calculateSystemContributions(snapshot.products);
325
+ const activeSegments = segments.filter((segment) => segment.state === 'active');
326
+ const activeBlockIds = new Set(activeSegments.flatMap((segment) => segment.active_block_id === null ? [] : [segment.active_block_id]));
327
+ const longestSegments = activeSegments.filter((segment) => segment.is_longest_active);
328
+ const longestRange = longestSegments.length
329
+ ? { start_ms: longestSegments[0].start_ms, end_ms: longestSegments.at(-1).end_ms }
330
+ : null;
331
+ const excludedSeconds = segments
332
+ .filter((segment) => segment.state === 'excluded')
333
+ .reduce((sum, segment) => sum + segment.duration_seconds, 0);
334
+ const extent = segments.length > 0
335
+ ? { start_ms: segments[0].start_ms, end_ms: segments.at(-1).end_ms }
336
+ : null;
337
+ const systems = systemActivitySummaries(snapshot.products, segments, contributions, snapshot.sample_interval_ms, resourceHistory);
338
+ return {
339
+ segments,
340
+ contributions,
341
+ insights: insights.map((insight) => ({
342
+ ...insight,
343
+ evidence: insightEvidenceTarget(insight.kind, segments, contributions),
344
+ })),
345
+ extent,
346
+ active_block_count: activeBlockIds.size,
347
+ longest_active_seconds: longestRange ? Math.max(0, longestRange.end_ms - longestRange.start_ms) / 1000 : 0,
348
+ longest_active_range: longestRange,
349
+ idle_seconds: Math.max(0, snapshot.observed_seconds - snapshot.active_seconds),
350
+ excluded_seconds: excludedSeconds,
351
+ simultaneous_systems_max: activeSegments.reduce((maximum, segment) => Math.max(maximum, segment.system_signatures.length), 0),
352
+ active_tool_count: systems.filter((system) => system.active_seconds > 0 || system.state === 'working-now').length,
353
+ observation_ids: [...new Set(segments.flatMap((segment) => segment.observation_id === null ? [] : [segment.observation_id]))].sort((left, right) => left - right),
354
+ continuity_ids: [...new Set(segments.flatMap((segment) => segment.continuity_id === null ? [] : [segment.continuity_id]))].sort((left, right) => left - right),
355
+ available_date_keys: availableLocalDateKeys(segments),
356
+ systems,
357
+ current_resources: aggregateCurrentAiResources(snapshot.products),
358
+ resource_history: resourceHistory,
359
+ resource_history_state: resourceHistory.buckets.length > 0
360
+ ? 'retained-local-buckets'
361
+ : snapshot.lifecycle === 'monitoring' ? 'collecting' : 'not-retained',
362
+ };
363
+ }
364
+ //# sourceMappingURL=activity-view-model.js.map
package/dist/agents.d.ts CHANGED
@@ -9,7 +9,9 @@ export interface ClassifiedAgentProcess {
9
9
  relationship: 'direct' | 'descendant';
10
10
  cpu_percent: number | null;
11
11
  memory_percent: number | null;
12
+ memory_bytes: number | null;
12
13
  cpu_time_ms: number | null;
14
+ activity_signal?: boolean;
13
15
  }
14
16
  /**
15
17
  * Builds private process-tree evidence for the activity monitor. PIDs and raw
package/dist/agents.js CHANGED
@@ -13,6 +13,10 @@ const node_util_1 = require("node:util");
13
13
  const hash_1 = require("./hash");
14
14
  const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
15
15
  const SIGNATURES = [
16
+ { signature: 'chatgpt', name: 'ChatGPT', kind: 'ai-app', terms: ['chatgpt'], executables: ['chatgpt'] },
17
+ { signature: 'jan', name: 'Jan', kind: 'ai-app', terms: ['jan'], executables: ['jan'] },
18
+ { signature: 'lm-studio', name: 'LM Studio', kind: 'ai-app', terms: ['lm-studio', 'lmstudio', 'lm studio'], executables: ['lm-studio', 'lmstudio', 'lm studio'] },
19
+ { signature: 'open-webui', name: 'Open WebUI', kind: 'ai-app', terms: ['open-webui', 'open_webui'], executables: ['open-webui', 'open_webui'] },
16
20
  { signature: 'codex', name: 'Codex', kind: 'coding-agent', terms: ['codex'], executables: ['codex'] },
17
21
  { signature: 'claude', name: 'Claude Code', kind: 'coding-agent', terms: ['claude', 'claude-code'], executables: ['claude'] },
18
22
  { signature: 'aider', name: 'Aider', kind: 'coding-agent', terms: ['aider'], executables: ['aider'] },
@@ -35,6 +39,13 @@ const SIGNATURES = [
35
39
  { signature: 'pydantic-ai', name: 'PydanticAI', kind: 'agent-framework', terms: ['pydantic-ai', 'pydantic_ai'], executables: ['pydantic-ai', 'pydantic_ai'] },
36
40
  { signature: 'smolagents', name: 'smolagents', kind: 'agent-framework', terms: ['smolagents'], executables: ['smolagents'] },
37
41
  ];
42
+ const CHATGPT_CODEX_SIGNATURE = {
43
+ signature: 'chatgpt-codex',
44
+ name: 'ChatGPT · Codex',
45
+ kind: 'ai-app',
46
+ terms: ['chatgpt-codex'],
47
+ executables: ['chatgpt', 'codex'],
48
+ };
38
49
  function basename(value) {
39
50
  const normalized = String(value || '').replaceAll('\\', '/');
40
51
  return node_path_1.default.posix.basename(normalized).toLowerCase().replace(/\.exe$/i, '');
@@ -44,6 +55,15 @@ function commandParts(value) {
44
55
  .match(/"[^"]*"|'[^']*'|\S+/g)
45
56
  ?.map((part) => part.replace(/^(?:"|')|(?:"|')$/g, '')) ?? [];
46
57
  }
58
+ function macosAppExecutableName(command) {
59
+ const marker = '.app/Contents/MacOS/';
60
+ const markerIndex = command.indexOf(marker);
61
+ if (markerIndex < 0)
62
+ return null;
63
+ const tail = command.slice(markerIndex + marker.length);
64
+ const executable = tail.split(/\s--/)[0]?.trim();
65
+ return executable ? basename(executable) : null;
66
+ }
47
67
  function moduleName(parts) {
48
68
  const moduleFlag = parts.findIndex((part) => part === '-m');
49
69
  if (moduleFlag < 0)
@@ -77,12 +97,36 @@ function nodeBinName(parts) {
77
97
  return null;
78
98
  return basename(script);
79
99
  }
100
+ function isNonAiSupportProcess(processName, executable, command) {
101
+ const supportText = `${processName} ${executable} ${command}`.toLowerCase();
102
+ return supportText.includes('browser_crashpad_handler')
103
+ || supportText.includes('crashpad_handler')
104
+ || processName === 'autoupdate'
105
+ || processName === 'updater'
106
+ || executable === 'autoupdate'
107
+ || executable === 'updater';
108
+ }
109
+ function isPassiveAppHelperProcess(entry) {
110
+ const processText = `${entry.name ?? ''} ${entry.cmd ?? ''}`.toLowerCase();
111
+ return processText.includes('codex (renderer)')
112
+ || processText.includes('codex (service)')
113
+ || processText.includes('--type=renderer')
114
+ || processText.includes('--type=utility')
115
+ || processText.includes('--type=gpu-process')
116
+ || processText.includes('browser_crashpad_handler')
117
+ || processText.includes('crashpad_handler')
118
+ || processText.includes('/sparkle.framework/')
119
+ || /\bautoupdate\b/.test(processText)
120
+ || /\bupdater\b/.test(processText);
121
+ }
80
122
  function classifyProcess(entry) {
81
123
  const processName = basename(entry.name ?? '');
82
124
  const command = String(entry.cmd ?? '');
83
125
  const parts = commandParts(command);
84
126
  const firstCommandToken = parts[0] ?? '';
85
- const executable = basename(firstCommandToken || processName) || 'unknown';
127
+ const executable = macosAppExecutableName(command) ?? (basename(firstCommandToken || processName) || 'unknown');
128
+ if (isNonAiSupportProcess(processName, executable, command))
129
+ return null;
86
130
  const invokedModule = moduleName(parts);
87
131
  const invokedPackage = packageRunnerName(parts) ?? nodeBinName(parts);
88
132
  for (const signature of SIGNATURES) {
@@ -125,6 +169,57 @@ function descendantsOf(processes, rootPid) {
125
169
  }
126
170
  return excluded;
127
171
  }
172
+ function nearestDirectAncestor(byPid, direct, start, signature) {
173
+ const visited = new Set();
174
+ let parentPid = start.ppid;
175
+ for (let depth = 0; parentPid !== undefined && parentPid > 0 && depth < 24; depth += 1) {
176
+ if (visited.has(parentPid))
177
+ break;
178
+ visited.add(parentPid);
179
+ const owner = direct.get(parentPid);
180
+ if (owner?.signature.signature === signature)
181
+ return parentPid;
182
+ parentPid = byPid.get(parentPid)?.ppid;
183
+ }
184
+ return null;
185
+ }
186
+ function asEmbeddedChatGptCodex(evidence) {
187
+ return {
188
+ ...evidence,
189
+ signature: CHATGPT_CODEX_SIGNATURE,
190
+ confidence: 'high',
191
+ reason: 'embedded_chatgpt_codex_process_tree',
192
+ evidenceHash: (0, hash_1.sha256)(`${CHATGPT_CODEX_SIGNATURE.signature}:${evidence.executable}:${evidence.reason}`),
193
+ };
194
+ }
195
+ function collapseEmbeddedOpenAiAppStack(processes, direct) {
196
+ const byPid = new Map(processes.map((entry) => [entry.pid, entry]));
197
+ const chatGptRootsWithCodex = new Set();
198
+ const embeddedCodexPids = new Set();
199
+ for (const entry of processes) {
200
+ const owner = direct.get(entry.pid);
201
+ if (owner?.signature.signature !== 'codex')
202
+ continue;
203
+ const chatGptRoot = nearestDirectAncestor(byPid, direct, entry, 'chatgpt');
204
+ if (chatGptRoot === null)
205
+ continue;
206
+ chatGptRootsWithCodex.add(chatGptRoot);
207
+ embeddedCodexPids.add(entry.pid);
208
+ }
209
+ if (chatGptRootsWithCodex.size === 0)
210
+ return direct;
211
+ const collapsed = new Map();
212
+ for (const [pid, evidence] of direct) {
213
+ if ((evidence.signature.signature === 'chatgpt' && chatGptRootsWithCodex.has(pid))
214
+ || (evidence.signature.signature === 'codex' && embeddedCodexPids.has(pid))) {
215
+ collapsed.set(pid, asEmbeddedChatGptCodex(evidence));
216
+ }
217
+ else {
218
+ collapsed.set(pid, evidence);
219
+ }
220
+ }
221
+ return collapsed;
222
+ }
128
223
  /**
129
224
  * Builds private process-tree evidence for the activity monitor. PIDs and raw
130
225
  * commands remain internal and are deliberately absent from public reports.
@@ -140,11 +235,12 @@ function classifyAgentProcessTrees(processes, excludedRootPid = process.pid) {
140
235
  if (evidence)
141
236
  direct.set(entry.pid, evidence);
142
237
  }
238
+ const collapsedDirect = collapseEmbeddedOpenAiAppStack(processes, direct);
143
239
  const result = [];
144
240
  for (const entry of processes) {
145
241
  if (excluded.has(entry.pid))
146
242
  continue;
147
- let owner = direct.get(entry.pid) ?? null;
243
+ let owner = collapsedDirect.get(entry.pid) ?? null;
148
244
  let relationship = 'direct';
149
245
  if (!owner) {
150
246
  const visited = new Set();
@@ -153,7 +249,7 @@ function classifyAgentProcessTrees(processes, excludedRootPid = process.pid) {
153
249
  if (visited.has(parentPid) || excluded.has(parentPid))
154
250
  break;
155
251
  visited.add(parentPid);
156
- owner = direct.get(parentPid) ?? null;
252
+ owner = collapsedDirect.get(parentPid) ?? null;
157
253
  if (owner) {
158
254
  relationship = 'descendant';
159
255
  break;
@@ -173,7 +269,9 @@ function classifyAgentProcessTrees(processes, excludedRootPid = process.pid) {
173
269
  relationship,
174
270
  cpu_percent: Number.isFinite(entry.cpu_percent) ? Math.max(0, Number(entry.cpu_percent)) : null,
175
271
  memory_percent: Number.isFinite(entry.memory_percent) ? Math.max(0, Number(entry.memory_percent)) : null,
272
+ memory_bytes: Number.isFinite(entry.rss_bytes) ? Math.max(0, Number(entry.rss_bytes)) : null,
176
273
  cpu_time_ms: Number.isFinite(entry.cpu_time_ms) ? Math.max(0, Number(entry.cpu_time_ms)) : null,
274
+ activity_signal: !isPassiveAppHelperProcess(entry),
177
275
  });
178
276
  }
179
277
  return result.sort((left, right) => left.signature.localeCompare(right.signature) || left.pid - right.pid);
@@ -220,11 +318,15 @@ function confidenceRank(confidence) {
220
318
  return confidence === 'high' ? 3 : confidence === 'medium' ? 2 : 1;
221
319
  }
222
320
  function detectAgentProducts(processes) {
223
- const grouped = new Map();
321
+ const direct = new Map();
224
322
  for (const process of processes) {
225
323
  const evidence = classifyProcess(process);
226
- if (!evidence)
227
- continue;
324
+ if (evidence)
325
+ direct.set(process.pid, evidence);
326
+ }
327
+ const collapsedDirect = collapseEmbeddedOpenAiAppStack(processes, direct);
328
+ const grouped = new Map();
329
+ for (const evidence of collapsedDirect.values()) {
228
330
  grouped.set(evidence.signature.signature, [
229
331
  ...(grouped.get(evidence.signature.signature) ?? []),
230
332
  evidence,
File without changes