windows-development-sandbox 1.11.7 → 1.12.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.
Files changed (121) hide show
  1. package/README.md +12 -6
  2. package/config.example.json +4 -3
  3. package/dist/app/features/apps/apps-rows.d.ts +4 -1
  4. package/dist/app/features/apps/apps-rows.js +7 -2
  5. package/dist/app/features/apps/apps.def.d.ts +19 -0
  6. package/dist/app/features/apps/apps.def.js +49 -12
  7. package/dist/app/features/apps/apps.run.d.ts +2 -0
  8. package/dist/app/features/apps/apps.run.js +1 -1
  9. package/dist/app/features/audit/audit.def.d.ts +341 -0
  10. package/dist/app/features/audit/audit.def.js +454 -0
  11. package/dist/app/features/audit/audit.run.d.ts +107 -0
  12. package/dist/app/features/audit/audit.run.js +133 -0
  13. package/dist/app/features/audit/index.d.ts +2 -0
  14. package/dist/app/features/audit/index.js +3 -0
  15. package/dist/app/features/defs.d.ts +194 -1
  16. package/dist/app/features/defs.js +4 -0
  17. package/dist/app/features/settings/settings.run.d.ts +3 -0
  18. package/dist/app/features/settings/settings.run.js +16 -2
  19. package/dist/app/kernel/define.d.ts +12 -1
  20. package/dist/app/registry.js +2 -0
  21. package/dist/cli/args.d.ts +0 -3
  22. package/dist/cli/args.js +3 -4
  23. package/dist/cli/commands/index.d.ts +1 -1
  24. package/dist/cli/commands/index.js +1 -1
  25. package/dist/cli/commands/launch/prepare.js +1 -1
  26. package/dist/cli/commands/maintenance/index.d.ts +0 -1
  27. package/dist/cli/commands/maintenance/index.js +0 -1
  28. package/dist/cli/commands/registry.js +1 -2
  29. package/dist/cli/manifest.d.ts +0 -41
  30. package/dist/cli/manifest.js +0 -14
  31. package/dist/cli/menu/pages/apps-page.js +2 -15
  32. package/dist/cli/menu/pages/maintenance-page.d.ts +4 -0
  33. package/dist/cli/menu/pages/maintenance-page.js +51 -8
  34. package/dist/cli/menu/views.js +2 -0
  35. package/dist/core/config/defaults.js +1 -1
  36. package/dist/core/schema/apps.d.ts +9 -0
  37. package/dist/core/schema/apps.js +9 -0
  38. package/dist/core/schema/config.d.ts +1 -0
  39. package/dist/core/schema/config.js +3 -3
  40. package/dist/core/schema/sandbox.d.ts +1 -0
  41. package/dist/core/schema/sandbox.js +11 -3
  42. package/dist/guest/apps/app-installer.js +50 -4
  43. package/dist/guest/apps/index.d.ts +1 -1
  44. package/dist/guest/apps/index.js +1 -1
  45. package/dist/guest/apps/sources.js +1 -1
  46. package/dist/guest/audit/parse.d.ts +32 -2
  47. package/dist/guest/audit/parse.js +43 -5
  48. package/dist/guest/audit/process-audit.d.ts +3 -1
  49. package/dist/guest/audit/process-audit.js +9 -6
  50. package/dist/guest/boot/boot.js +12 -3
  51. package/dist/guest/boot/guide.d.ts +1 -1
  52. package/dist/guest/boot/guide.js +27 -3
  53. package/dist/guest/daemon/sessions.d.ts +7 -0
  54. package/dist/guest/daemon/sessions.js +27 -2
  55. package/dist/guest/entries/daemon.js +2 -1
  56. package/dist/guest/setup/addon-mcp.d.ts +2 -0
  57. package/dist/guest/setup/addon-mcp.js +49 -0
  58. package/dist/guest/setup/agent-addons.d.ts +9 -0
  59. package/dist/guest/setup/agent-addons.js +22 -0
  60. package/dist/guest/setup/headroom.d.ts +7 -0
  61. package/dist/guest/setup/headroom.js +60 -0
  62. package/dist/guest/setup/managed-policy.d.ts +2 -1
  63. package/dist/guest/setup/managed-policy.js +15 -6
  64. package/dist/guest/toolchains/plan.js +8 -3
  65. package/dist/host/audit/attribute.d.ts +46 -0
  66. package/dist/host/audit/attribute.js +76 -0
  67. package/dist/host/audit/collect.d.ts +29 -3
  68. package/dist/host/audit/collect.js +256 -61
  69. package/dist/host/audit/git-commits.d.ts +29 -0
  70. package/dist/host/audit/git-commits.js +157 -0
  71. package/dist/host/audit/index.d.ts +5 -0
  72. package/dist/host/audit/index.js +6 -0
  73. package/dist/host/audit/log.d.ts +44 -7
  74. package/dist/host/audit/log.js +148 -45
  75. package/dist/host/audit/redact.d.ts +2 -0
  76. package/dist/host/audit/redact.js +67 -0
  77. package/dist/runtime/agent.js +110 -15
  78. package/dist/runtime/daemon.js +569 -316
  79. package/dist/runtime/db.js +110 -15
  80. package/dist/runtime/main.js +1011 -672
  81. package/dist/runtime/netproxy.js +110 -15
  82. package/dist/runtime/view.js +110 -15
  83. package/dist/selftest/app.test.js +25 -1
  84. package/dist/selftest/audit.test.js +354 -47
  85. package/dist/selftest/hcs.test.js +35 -0
  86. package/dist/selftest/menu-screens.js +1 -1
  87. package/dist/selftest/menu.test.js +3 -3
  88. package/dist/selftest/support/engine-golden.js +9 -0
  89. package/dist/selftest/support/fixtures.d.ts +1 -0
  90. package/dist/selftest/support/web-preview.js +94 -0
  91. package/dist/selftest/toolchains.test.js +147 -2
  92. package/dist/shared/apps/agent-addons.d.ts +64 -0
  93. package/dist/shared/apps/agent-addons.js +95 -0
  94. package/dist/shared/apps/catalogue.d.ts +33 -2
  95. package/dist/shared/apps/catalogue.js +102 -11
  96. package/dist/shared/apps/index.d.ts +3 -2
  97. package/dist/shared/apps/index.js +3 -2
  98. package/dist/shared/apps/selection.d.ts +2 -0
  99. package/dist/shared/apps/selection.js +7 -0
  100. package/dist/sync/watcher.js +9 -1
  101. package/dist/web/index.html +2 -2
  102. package/dist/web/{main-RQFN4WBY.css → main-UAQPI5XD.css} +1 -1
  103. package/dist/web/{main-K2JMCAZI.js → main-UD4QCXZR.js} +66 -66
  104. package/native/csharp/HcsHost/AssemblyInfo.cs +5 -3
  105. package/native/csharp/HcsHost/Hcs.cs +9 -0
  106. package/native/csharp/HcsHost/Program.cs +26 -1
  107. package/native/csharp/HcsHost/Share.cs +173 -0
  108. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  109. package/package.json +1 -1
  110. package/sandbox/addons/ccusage/package-lock.json +111 -0
  111. package/sandbox/addons/ccusage/package.json +7 -0
  112. package/sandbox/addons/context7/package-lock.json +1313 -0
  113. package/sandbox/addons/context7/package.json +7 -0
  114. package/sandbox/addons/headroom/requirements.in +1 -0
  115. package/sandbox/addons/headroom/requirements.txt +2922 -0
  116. package/sandbox/addons/playwright-mcp/package-lock.json +56 -0
  117. package/sandbox/addons/playwright-mcp/package.json +7 -0
  118. package/schemas/config.schema.json +13 -4
  119. package/schemas/sandbox-config.schema.json +2 -2
  120. package/dist/cli/commands/maintenance/audit.d.ts +0 -3
  121. package/dist/cli/commands/maintenance/audit.js +0 -58
@@ -0,0 +1,454 @@
1
+ // The audit log as a page: what the agents ran, changed and committed, per session, from the log on this PC
2
+ // (audit.enabled). Pure: schemas, labels and the view; audit.run.ts reads the log. Filters are the query's input: the
3
+ // command line has them as flags (sandbox audit --days 7 --session <id>), the web as the view's filters and
4
+ // parameters, and each session's card opens the page filtered to it.
5
+ import { z } from 'zod';
6
+ import { action, ago, bytesText, defineQuery, defineView, link, plural, } from '../../kernel/index.js';
7
+ import { setSetting } from '../settings/settings.def.js';
8
+ export const AUDIT_EVENT_KINDS = ['proc', 'file', 'commit', 'session', 'guard', 'note'];
9
+ export const AUDIT_KIND_FILTERS = ['all', 'proc', 'file', 'commit', 'session', 'guard'];
10
+ /** The days the page offers (the log keeps at most 30). */
11
+ const PERIODS = [1, 3, 7, 14, 30];
12
+ /** The session filter that means "not linked to any session". */
13
+ export const NO_SESSION = 'none';
14
+ /** The longest "what" a table cell shows (the whole text is in the details and in --json). */
15
+ const CELL_MAX = 240;
16
+ const KIND_LABEL = {
17
+ proc: 'command',
18
+ file: 'file',
19
+ commit: 'commit',
20
+ session: 'session',
21
+ guard: 'GUARD',
22
+ note: 'note',
23
+ };
24
+ export const AuditRowSchema = z.object({
25
+ time: z.string().describe('When it happened (ISO time)'),
26
+ kind: z
27
+ .enum(AUDIT_EVENT_KINDS)
28
+ .describe('proc: a program started; file: changed in a project; commit; session; guard: the repo guard; note'),
29
+ what: z.string().describe('The program, the file, the commit message, the agent, or what happened'),
30
+ command: z.string().optional().describe("A program's command line (secrets hidden)"),
31
+ parent: z.string().optional().describe('The program that started it'),
32
+ action: z
33
+ .string()
34
+ .optional()
35
+ .describe('file: change or rename; session: start or end; commit: commit, amend, merge, cherry-pick ...'),
36
+ folder: z.string().optional().describe('The project folder on this PC'),
37
+ project: z.string().optional().describe('The project'),
38
+ session: z.string().optional().describe('The agent session it belongs to (missing: not known)'),
39
+ agent: z.string().optional().describe("That session's agent"),
40
+ linked: z
41
+ .enum(['process', 'only-session'])
42
+ .optional()
43
+ .describe('How it was linked: process = by the programs the session started; only-session = the only session running in that project then (likely, not proven)'),
44
+ count: z.number().int().describe('How many times it happened in that minute'),
45
+ sha: z.string().optional().describe("A commit's id"),
46
+ author: z.string().optional().describe("A commit's author"),
47
+ });
48
+ const CountsSchema = z.object({
49
+ commands: z.number().int().describe('Programs started'),
50
+ files: z.number().int().describe('Files changed (each file once)'),
51
+ commits: z.number().int().describe('Commits'),
52
+ });
53
+ export const AuditSessionSchema = CountsSchema.extend({
54
+ id: z.string().describe('The session'),
55
+ agent: z.string().describe('Its agent'),
56
+ project: z.string().describe('Its project'),
57
+ started: z.string().describe('Its first event (ISO time)'),
58
+ ended: z.string().optional().describe('When it ended, if that was recorded'),
59
+ last: z.string().optional().describe('The last command it ran'),
60
+ });
61
+ export const AuditAgentSchema = CountsSchema.extend({
62
+ agent: z.string().describe('The agent'),
63
+ sessions: z.number().int().describe('Its sessions'),
64
+ });
65
+ export const AuditListSchema = z.object({
66
+ enabled: z.boolean().describe('The audit log is on (audit.enabled)'),
67
+ keepDays: z.number().int().describe('Days it is kept (audit.days)'),
68
+ maxMb: z.number().int().describe('Most space it may take (audit.maxMb)'),
69
+ bytes: z.number().describe('Space it takes now'),
70
+ folder: z.string().describe('Where it is on this PC'),
71
+ filter: z.object({
72
+ days: z.number().int(),
73
+ kind: z.enum(AUDIT_KIND_FILTERS),
74
+ session: z.string().optional(),
75
+ agent: z.string().optional(),
76
+ project: z.string().optional(),
77
+ grep: z.string().optional(),
78
+ }),
79
+ /** Events that match every filter (the list holds the newest of them). */
80
+ matched: z.number().int(),
81
+ events: z.array(AuditRowSchema).describe('The events, newest first'),
82
+ /** Over the period and filters, every kind. */
83
+ totals: CountsSchema.extend({ events: z.number().int(), sessions: z.number().int() }),
84
+ agents: z.array(AuditAgentSchema),
85
+ sessions: z.array(AuditSessionSchema).describe('The sessions in the period, newest first'),
86
+ /** What no session can be linked to. */
87
+ unlinked: CountsSchema,
88
+ });
89
+ export const listAudit = defineQuery({
90
+ id: 'audit.list',
91
+ title: 'Activity',
92
+ summary: 'what your agents did: programs started, files changed, commits, sessions, repo guard',
93
+ input: z.object({
94
+ days: z.number().int().min(1).max(30).default(1).describe('the last n days (1: the last 24 hours)'),
95
+ kind: z.enum(AUDIT_KIND_FILTERS).default('all').describe('only proc, file, commit, session or guard events'),
96
+ session: z
97
+ .string()
98
+ .max(64)
99
+ .optional()
100
+ .describe('only the events of this session (its id or the start of it; none: not linked to a session)'),
101
+ agent: z.string().max(64).optional().describe("only this agent's events"),
102
+ project: z.string().max(128).optional().describe("only this project's events"),
103
+ grep: z.string().max(200).optional().describe('only events containing this text'),
104
+ limit: z.number().int().min(1).max(5000).default(300).describe('show at most this many events (the newest)'),
105
+ }),
106
+ output: AuditListSchema,
107
+ live: { everyMs: 15000 },
108
+ cli: {
109
+ name: 'audit',
110
+ group: 'Health',
111
+ json: true,
112
+ flags: {
113
+ days: { flag: '--days', value: 'n' },
114
+ kind: { flag: '--kind', value: 'kind' },
115
+ session: { flag: '--session', value: 'id' },
116
+ agent: { flag: '--agent', value: 'name' },
117
+ project: { flag: '--project', value: 'name' },
118
+ grep: { flag: '--grep', value: 'text' },
119
+ limit: { flag: '--limit', value: 'n', advanced: true },
120
+ },
121
+ // Oldest first, like the log itself.
122
+ shapeJson: (o) => [...o.events].reverse(),
123
+ examples: ['sandbox audit --days 7 --kind commit', 'sandbox audit --session 4f2c --grep "git push"'],
124
+ },
125
+ });
126
+ /** "14:03:22" today, "Oct 18 14:03" before (local time). */
127
+ export function eventTime(iso, now = Date.now()) {
128
+ const d = new Date(iso);
129
+ if (Number.isNaN(d.getTime()))
130
+ return iso;
131
+ const two = (n) => String(n).padStart(2, '0');
132
+ const today = new Date(now).toDateString() === d.toDateString();
133
+ if (today)
134
+ return two(d.getHours()) + ':' + two(d.getMinutes()) + ':' + two(d.getSeconds());
135
+ const month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][d.getMonth()];
136
+ return month + ' ' + d.getDate() + ' ' + two(d.getHours()) + ':' + two(d.getMinutes());
137
+ }
138
+ /** The first few characters of a session id (enough to tell sessions apart, and to filter by). */
139
+ export function shortId(id) {
140
+ return id.slice(0, 8);
141
+ }
142
+ /** What an event says, in one line. */
143
+ export function eventWhat(e) {
144
+ switch (e.kind) {
145
+ case 'proc':
146
+ return e.command || e.what;
147
+ case 'file':
148
+ return (e.action === 'rename' ? 'added/removed ' : 'changed ') + e.what;
149
+ case 'commit':
150
+ return ((e.sha ? e.sha.slice(0, 7) + ' ' : '') +
151
+ e.what +
152
+ (e.action && e.action !== 'commit' ? ' (' + e.action + ')' : ''));
153
+ case 'session':
154
+ return e.what + (e.action === 'end' ? ' ended' : ' started') + (e.project ? ' in ' + e.project : '');
155
+ default:
156
+ return e.what;
157
+ }
158
+ }
159
+ /** Who did it: the agent, with ? when only likely, - when not known. */
160
+ function whoText(e) {
161
+ if (!e.agent)
162
+ return '-';
163
+ return e.agent + (e.linked === 'only-session' ? '?' : '');
164
+ }
165
+ function clip(text, max = CELL_MAX) {
166
+ return text.length > max ? text.slice(0, max - 1) + '…' : text;
167
+ }
168
+ function eventItem(e, index) {
169
+ const what = eventWhat(e) + (e.count > 1 ? ' ×' + e.count : '');
170
+ const details = [
171
+ ...(e.session ? ['Session ' + e.session + (e.linked === 'only-session' ? ' (likely)' : '')] : []),
172
+ ...(e.project ? ['Project ' + e.project] : []),
173
+ ...(e.kind === 'proc' && e.command && e.what !== e.command ? ['Program ' + e.what] : []),
174
+ ...(e.parent ? ['Started by ' + e.parent] : []),
175
+ ...(e.author ? ['Author ' + e.author] : []),
176
+ ...(e.sha ? ['Commit ' + e.sha] : []),
177
+ ...(e.folder && e.kind !== 'session' ? ['Folder ' + e.folder] : []),
178
+ ];
179
+ return {
180
+ id: 'event:' + index,
181
+ label: clip(what),
182
+ hint: eventTime(e.time) + ' ' + whoText(e),
183
+ tone: e.kind === 'guard' ? 'warn' : undefined,
184
+ details,
185
+ keywords: [e.session, e.project, e.agent, e.kind, e.author].filter(Boolean).join(' '),
186
+ cells: {
187
+ time: eventTime(e.time),
188
+ agent: whoText(e),
189
+ kind: KIND_LABEL[e.kind],
190
+ what: clip(what),
191
+ },
192
+ };
193
+ }
194
+ /** The address of the page with these filters (the period and kind kept unless given). */
195
+ function filtered(o, more) {
196
+ return { days: String(o.filter.days), kind: o.filter.kind, ...more };
197
+ }
198
+ /** "12 commands, 3 files, 1 commit". */
199
+ function countsText(c) {
200
+ return plural(c.commands, 'command') + ', ' + plural(c.files, 'file') + ', ' + plural(c.commits, 'commit');
201
+ }
202
+ function sessionItem(o, s) {
203
+ return {
204
+ id: 'session:' + s.id,
205
+ label: s.agent + ' ' + s.project,
206
+ icon: s.agent,
207
+ hint: 'started ' + ago(s.started) + (s.ended ? ', ended ' + ago(s.ended) : ''),
208
+ badges: [
209
+ ...(s.commits ? [{ text: plural(s.commits, 'commit'), tone: 'accent' }] : []),
210
+ ...(s.ended ? [{ text: 'ended', tone: 'muted' }] : []),
211
+ ],
212
+ details: ['Did ' + countsText(s), ...(s.last ? ['Last ' + clip(s.last, 120)] : []), 'Session ' + s.id],
213
+ keywords: s.id,
214
+ cells: {
215
+ session: shortId(s.id),
216
+ agent: s.agent,
217
+ project: s.project,
218
+ started: eventTime(s.started),
219
+ commands: String(s.commands),
220
+ files: String(s.files),
221
+ commits: String(s.commits),
222
+ },
223
+ primary: link({ kind: 'view', view: 'audit', params: filtered(o, { session: s.id }) }, 'Show', {
224
+ verb: 'show',
225
+ description: 'Only what this session did.',
226
+ }),
227
+ };
228
+ }
229
+ function unlinkedItem(o) {
230
+ const u = o.unlinked;
231
+ return {
232
+ id: 'session:' + NO_SESSION,
233
+ label: 'Not linked to a session',
234
+ icon: 'session',
235
+ description: 'What the sandbox ran itself (setup, services), and changes made while no session or several ran in that project.',
236
+ details: ['Did ' + countsText(u)],
237
+ cells: {
238
+ session: NO_SESSION,
239
+ agent: '-',
240
+ project: '-',
241
+ started: '',
242
+ commands: String(u.commands),
243
+ files: String(u.files),
244
+ commits: String(u.commits),
245
+ },
246
+ primary: link({ kind: 'view', view: 'audit', params: filtered(o, { session: NO_SESSION }) }, 'Show', {
247
+ verb: 'show',
248
+ description: 'Only what no session can be linked to.',
249
+ }),
250
+ };
251
+ }
252
+ /** "session 4f2c1a9b (claude · app)", "agent codex", "text "git push"" for the filters in use. */
253
+ function filterText(o) {
254
+ const f = o.filter;
255
+ const session = f.session ? o.sessions.find((s) => s.id === f.session) : undefined;
256
+ return [
257
+ f.session === NO_SESSION
258
+ ? 'not linked to a session'
259
+ : f.session
260
+ ? 'session ' + shortId(f.session) + (session ? ' (' + session.agent + ', ' + session.project + ')' : '')
261
+ : '',
262
+ f.agent ? 'agent ' + f.agent : '',
263
+ f.project ? 'project ' + f.project : '',
264
+ f.grep ? 'containing "' + f.grep + '"' : '',
265
+ ]
266
+ .filter(Boolean)
267
+ .join(', ');
268
+ }
269
+ /** The sessions as cards (a table on the command line), or nothing when there are none or one is shown. */
270
+ function sessionsSection(o, items, period) {
271
+ if (!items.length)
272
+ return [];
273
+ return [
274
+ {
275
+ id: 'sessions',
276
+ title: 'Sessions',
277
+ summary: plural(o.sessions.length, 'session') + ' ' + period,
278
+ cards: true,
279
+ columnsOn: 'cli',
280
+ columns: [
281
+ { key: 'session', label: 'SESSION' },
282
+ { key: 'agent', label: 'AGENT' },
283
+ { key: 'project', label: 'PROJECT' },
284
+ { key: 'started', label: 'STARTED' },
285
+ { key: 'commands', label: 'COMMANDS' },
286
+ { key: 'files', label: 'FILES' },
287
+ { key: 'commits', label: 'COMMITS' },
288
+ ],
289
+ items,
290
+ notes: [{ text: 'Only one session: sandbox audit --session <id>', tone: 'muted', only: 'cli' }],
291
+ },
292
+ ];
293
+ }
294
+ /** The page: the period's totals, per agent, the sessions (each opens its own events), and the events. Pure. */
295
+ export function auditPage(o) {
296
+ const f = o.filter;
297
+ const period = f.days === 1 ? 'in the last 24 hours' : 'in the last ' + f.days + ' days';
298
+ const narrowed = Boolean(f.session || f.agent || f.project || f.grep);
299
+ const showAll = link({ kind: 'view', view: 'audit', params: filtered(o, {}) }, 'Show everything', { verb: 'show' });
300
+ const turnOn = action(setSetting, { key: 'audit.enabled', value: true }, { label: 'Turn the audit log on', verb: 'start', key: 'o', importance: 1 });
301
+ const t = o.totals;
302
+ const summary = t.events
303
+ ? [
304
+ { id: 'events', label: 'Events', value: String(t.events) },
305
+ { id: 'commands', label: 'Commands', value: String(t.commands) },
306
+ { id: 'files', label: 'Files changed', value: String(t.files) },
307
+ {
308
+ id: 'commits',
309
+ label: 'Commits',
310
+ value: String(t.commits),
311
+ ...(t.commits ? { tone: 'accent' } : {}),
312
+ },
313
+ { id: 'sessions', label: 'Sessions', value: String(t.sessions) },
314
+ ]
315
+ : [];
316
+ const agents = f.session || o.agents.length < 1
317
+ ? []
318
+ : o.agents.map((a) => ({
319
+ id: 'agent:' + a.agent,
320
+ label: a.agent,
321
+ value: plural(a.commands, 'command'),
322
+ details: [plural(a.files, 'file') + ', ' + plural(a.commits, 'commit'), plural(a.sessions, 'session')],
323
+ }));
324
+ const logNote = {
325
+ text: 'The log takes ' +
326
+ bytesText(o.bytes) +
327
+ ' of ' +
328
+ o.maxMb +
329
+ ' MB and keeps ' +
330
+ plural(o.keepDays, 'day') +
331
+ ', on this PC where the sandbox cannot reach it (' +
332
+ o.folder +
333
+ ').',
334
+ tone: 'muted',
335
+ };
336
+ const sessionItems = f.session !== undefined
337
+ ? []
338
+ : [
339
+ ...o.sessions.map((s) => sessionItem(o, s)),
340
+ ...(o.unlinked.commands || o.unlinked.files || o.unlinked.commits ? [unlinkedItem(o)] : []),
341
+ ];
342
+ const events = o.events.map(eventItem);
343
+ const likely = o.events.some((e) => e.linked === 'only-session');
344
+ const sections = [
345
+ ...(narrowed ? [{ id: 'filter', title: 'Filtered', summary: filterText(o), actions: [showAll] }] : []),
346
+ { id: 'summary', facts: summary },
347
+ ...(agents.length ? [{ id: 'agents', title: 'By agent', facts: agents }] : []),
348
+ ...sessionsSection(o, sessionItems, period),
349
+ {
350
+ id: 'events',
351
+ title: 'Events',
352
+ summary: o.matched > events.length
353
+ ? 'the newest ' + events.length + ' of ' + o.matched
354
+ : plural(o.matched, 'event') + ' ' + period,
355
+ columns: [
356
+ { key: 'time', label: 'TIME' },
357
+ { key: 'agent', label: 'AGENT' },
358
+ { key: 'kind', label: 'KIND' },
359
+ { key: 'what', label: 'WHAT' },
360
+ ],
361
+ items: events,
362
+ notes: [
363
+ logNote,
364
+ ...(likely
365
+ ? [
366
+ {
367
+ text: '? = the only session running in that project then (likely, not proven)',
368
+ tone: 'muted',
369
+ },
370
+ ]
371
+ : []),
372
+ ...(o.matched > events.length
373
+ ? [
374
+ {
375
+ text: 'More: sandbox audit --limit <n>, or narrow it down.',
376
+ tone: 'muted',
377
+ only: 'cli',
378
+ },
379
+ ]
380
+ : []),
381
+ ],
382
+ },
383
+ ];
384
+ return {
385
+ title: 'Activity',
386
+ ...(o.enabled ? {} : { notice: { text: 'The audit log is off: nothing new is recorded.', tone: 'warn' } }),
387
+ sections,
388
+ actions: o.enabled ? [] : [turnOn],
389
+ empty: {
390
+ title: narrowed ? 'Nothing matches' : o.enabled ? 'Nothing recorded ' + period : 'The audit log is off',
391
+ text: narrowed
392
+ ? 'No events ' + period + ' for ' + filterText(o) + '.'
393
+ : o.enabled
394
+ ? 'What your agents run, change and commit appears here while a sandbox runs.'
395
+ : 'Turn it on to keep a record of what your agents run, change and commit (kept ' +
396
+ plural(o.keepDays, 'day') +
397
+ ' on this PC).',
398
+ action: narrowed ? showAll : o.enabled ? undefined : turnOn,
399
+ cliText: o.enabled
400
+ ? 'Nothing in the audit log ' + period + (narrowed ? ' for ' + filterText(o) : '') + '.'
401
+ : 'The audit log is off. Turn it on in the menu (Settings > Security & privacy > Audit log) or with: sandbox config set audit.enabled true',
402
+ },
403
+ };
404
+ }
405
+ /** The command line that shows what a view address of this page shows (the menu runs it on the normal screen). */
406
+ export function auditArgv(params = {}) {
407
+ const flags = [
408
+ ['--days', params.days],
409
+ ['--kind', params.kind === 'all' ? undefined : params.kind],
410
+ ['--session', params.session],
411
+ ['--agent', params.agent],
412
+ ['--project', params.project],
413
+ ['--grep', params.grep],
414
+ ];
415
+ return ['audit', ...flags.flatMap(([flag, value]) => (value ? [flag, value] : []))];
416
+ }
417
+ export const auditView = defineView({
418
+ id: 'audit',
419
+ title: 'Activity',
420
+ summary: 'What your agents ran, changed and committed, from the audit log on this PC.',
421
+ queries: { audit: listAudit },
422
+ operations: [setSetting],
423
+ filters: [
424
+ {
425
+ param: 'kind',
426
+ label: 'Kind',
427
+ choices: [
428
+ { value: 'all', label: 'Everything' },
429
+ { value: 'proc', label: 'Commands' },
430
+ { value: 'file', label: 'Files' },
431
+ { value: 'commit', label: 'Commits' },
432
+ { value: 'session', label: 'Sessions' },
433
+ { value: 'guard', label: 'Repo guard' },
434
+ ],
435
+ },
436
+ {
437
+ param: 'days',
438
+ label: 'Period',
439
+ choices: PERIODS.map((d) => ({ value: String(d), label: d === 1 ? '24 hours' : d + ' days' })),
440
+ },
441
+ ],
442
+ params: ['session', 'agent', 'project', 'grep'],
443
+ inputs: (p) => ({
444
+ audit: {
445
+ days: PERIODS.find((d) => String(d) === p.days) ?? 1,
446
+ kind: AUDIT_KIND_FILTERS.find((k) => k === p.kind) ?? 'all',
447
+ ...(p.session ? { session: p.session } : {}),
448
+ ...(p.agent ? { agent: p.agent } : {}),
449
+ ...(p.project ? { project: p.project } : {}),
450
+ ...(p.grep ? { grep: p.grep } : {}),
451
+ },
452
+ }),
453
+ build: ({ audit }) => auditPage(audit),
454
+ });
@@ -0,0 +1,107 @@
1
+ import { AuditLog, type AuditEvent } from '../../../host/audit/index.js';
2
+ import type { InputOf } from '../../kernel/index.js';
3
+ import { listAudit, type AuditList, type AuditRow } from './audit.def.js';
4
+ type AuditInput = InputOf<typeof listAudit>;
5
+ /** A stored event as the feature shows it. */
6
+ export declare function auditRow(e: AuditEvent): AuditRow;
7
+ /** The listing for these filters, from a log (the command line, the menu and the web all show this). */
8
+ export declare function auditListing(log: AuditLog, settings: {
9
+ readonly enabled: boolean;
10
+ readonly days: number;
11
+ readonly maxMb: number;
12
+ }, input: AuditInput, folder: string, now?: number): AuditList;
13
+ export declare const listAuditRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").QueryDef<import("zod").ZodObject<{
14
+ days: import("zod").ZodDefault<import("zod").ZodNumber>;
15
+ kind: import("zod").ZodDefault<import("zod").ZodEnum<{
16
+ file: "file";
17
+ all: "all";
18
+ session: "session";
19
+ commit: "commit";
20
+ proc: "proc";
21
+ guard: "guard";
22
+ }>>;
23
+ session: import("zod").ZodOptional<import("zod").ZodString>;
24
+ agent: import("zod").ZodOptional<import("zod").ZodString>;
25
+ project: import("zod").ZodOptional<import("zod").ZodString>;
26
+ grep: import("zod").ZodOptional<import("zod").ZodString>;
27
+ limit: import("zod").ZodDefault<import("zod").ZodNumber>;
28
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
29
+ enabled: import("zod").ZodBoolean;
30
+ keepDays: import("zod").ZodNumber;
31
+ maxMb: import("zod").ZodNumber;
32
+ bytes: import("zod").ZodNumber;
33
+ folder: import("zod").ZodString;
34
+ filter: import("zod").ZodObject<{
35
+ days: import("zod").ZodNumber;
36
+ kind: import("zod").ZodEnum<{
37
+ file: "file";
38
+ all: "all";
39
+ session: "session";
40
+ commit: "commit";
41
+ proc: "proc";
42
+ guard: "guard";
43
+ }>;
44
+ session: import("zod").ZodOptional<import("zod").ZodString>;
45
+ agent: import("zod").ZodOptional<import("zod").ZodString>;
46
+ project: import("zod").ZodOptional<import("zod").ZodString>;
47
+ grep: import("zod").ZodOptional<import("zod").ZodString>;
48
+ }, import("zod/v4/core").$strip>;
49
+ matched: import("zod").ZodNumber;
50
+ events: import("zod").ZodArray<import("zod").ZodObject<{
51
+ time: import("zod").ZodString;
52
+ kind: import("zod").ZodEnum<{
53
+ file: "file";
54
+ session: "session";
55
+ note: "note";
56
+ commit: "commit";
57
+ proc: "proc";
58
+ guard: "guard";
59
+ }>;
60
+ what: import("zod").ZodString;
61
+ command: import("zod").ZodOptional<import("zod").ZodString>;
62
+ parent: import("zod").ZodOptional<import("zod").ZodString>;
63
+ action: import("zod").ZodOptional<import("zod").ZodString>;
64
+ folder: import("zod").ZodOptional<import("zod").ZodString>;
65
+ project: import("zod").ZodOptional<import("zod").ZodString>;
66
+ session: import("zod").ZodOptional<import("zod").ZodString>;
67
+ agent: import("zod").ZodOptional<import("zod").ZodString>;
68
+ linked: import("zod").ZodOptional<import("zod").ZodEnum<{
69
+ process: "process";
70
+ "only-session": "only-session";
71
+ }>>;
72
+ count: import("zod").ZodNumber;
73
+ sha: import("zod").ZodOptional<import("zod").ZodString>;
74
+ author: import("zod").ZodOptional<import("zod").ZodString>;
75
+ }, import("zod/v4/core").$strip>>;
76
+ totals: import("zod").ZodObject<{
77
+ commands: import("zod").ZodNumber;
78
+ files: import("zod").ZodNumber;
79
+ commits: import("zod").ZodNumber;
80
+ events: import("zod").ZodNumber;
81
+ sessions: import("zod").ZodNumber;
82
+ }, import("zod/v4/core").$strip>;
83
+ agents: import("zod").ZodArray<import("zod").ZodObject<{
84
+ commands: import("zod").ZodNumber;
85
+ files: import("zod").ZodNumber;
86
+ commits: import("zod").ZodNumber;
87
+ agent: import("zod").ZodString;
88
+ sessions: import("zod").ZodNumber;
89
+ }, import("zod/v4/core").$strip>>;
90
+ sessions: import("zod").ZodArray<import("zod").ZodObject<{
91
+ commands: import("zod").ZodNumber;
92
+ files: import("zod").ZodNumber;
93
+ commits: import("zod").ZodNumber;
94
+ id: import("zod").ZodString;
95
+ agent: import("zod").ZodString;
96
+ project: import("zod").ZodString;
97
+ started: import("zod").ZodString;
98
+ ended: import("zod").ZodOptional<import("zod").ZodString>;
99
+ last: import("zod").ZodOptional<import("zod").ZodString>;
100
+ }, import("zod/v4/core").$strip>>;
101
+ unlinked: import("zod").ZodObject<{
102
+ commands: import("zod").ZodNumber;
103
+ files: import("zod").ZodNumber;
104
+ commits: import("zod").ZodNumber;
105
+ }, import("zod/v4/core").$strip>;
106
+ }, import("zod/v4/core").$strip>, "audit.list">, import("../../../cli/commands/context.js").Context>;
107
+ export {};