@aiassesstech/grillo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +21 -0
  3. package/README.md +512 -0
  4. package/SKILL.md +87 -0
  5. package/dist/api/server.d.ts +68 -0
  6. package/dist/api/server.d.ts.map +1 -0
  7. package/dist/api/server.js +596 -0
  8. package/dist/api/server.js.map +1 -0
  9. package/dist/audit/audit-log.d.ts +88 -0
  10. package/dist/audit/audit-log.d.ts.map +1 -0
  11. package/dist/audit/audit-log.js +195 -0
  12. package/dist/audit/audit-log.js.map +1 -0
  13. package/dist/certification/certificate.d.ts +80 -0
  14. package/dist/certification/certificate.d.ts.map +1 -0
  15. package/dist/certification/certificate.js +176 -0
  16. package/dist/certification/certificate.js.map +1 -0
  17. package/dist/cli/bin.d.ts +8 -0
  18. package/dist/cli/bin.d.ts.map +1 -0
  19. package/dist/cli/bin.js +12 -0
  20. package/dist/cli/bin.js.map +1 -0
  21. package/dist/cli/config-loader.d.ts +66 -0
  22. package/dist/cli/config-loader.d.ts.map +1 -0
  23. package/dist/cli/config-loader.js +243 -0
  24. package/dist/cli/config-loader.js.map +1 -0
  25. package/dist/cli/runner.d.ts +27 -0
  26. package/dist/cli/runner.d.ts.map +1 -0
  27. package/dist/cli/runner.js +388 -0
  28. package/dist/cli/runner.js.map +1 -0
  29. package/dist/commands/grillo-commands.d.ts +50 -0
  30. package/dist/commands/grillo-commands.d.ts.map +1 -0
  31. package/dist/commands/grillo-commands.js +752 -0
  32. package/dist/commands/grillo-commands.js.map +1 -0
  33. package/dist/commands/inline-commands.d.ts +16 -0
  34. package/dist/commands/inline-commands.d.ts.map +1 -0
  35. package/dist/commands/inline-commands.js +277 -0
  36. package/dist/commands/inline-commands.js.map +1 -0
  37. package/dist/commands/router.d.ts +56 -0
  38. package/dist/commands/router.d.ts.map +1 -0
  39. package/dist/commands/router.js +154 -0
  40. package/dist/commands/router.js.map +1 -0
  41. package/dist/config/defaults.d.ts +9 -0
  42. package/dist/config/defaults.d.ts.map +1 -0
  43. package/dist/config/defaults.js +78 -0
  44. package/dist/config/defaults.js.map +1 -0
  45. package/dist/config/schema.d.ts +573 -0
  46. package/dist/config/schema.d.ts.map +1 -0
  47. package/dist/config/schema.js +142 -0
  48. package/dist/config/schema.js.map +1 -0
  49. package/dist/dashboard/metrics.d.ts +100 -0
  50. package/dist/dashboard/metrics.d.ts.map +1 -0
  51. package/dist/dashboard/metrics.js +282 -0
  52. package/dist/dashboard/metrics.js.map +1 -0
  53. package/dist/dashboard/ui.d.ts +19 -0
  54. package/dist/dashboard/ui.d.ts.map +1 -0
  55. package/dist/dashboard/ui.js +951 -0
  56. package/dist/dashboard/ui.js.map +1 -0
  57. package/dist/discovery/discovery-adapter.d.ts +94 -0
  58. package/dist/discovery/discovery-adapter.d.ts.map +1 -0
  59. package/dist/discovery/discovery-adapter.js +114 -0
  60. package/dist/discovery/discovery-adapter.js.map +1 -0
  61. package/dist/discovery/discovery-service.d.ts +77 -0
  62. package/dist/discovery/discovery-service.d.ts.map +1 -0
  63. package/dist/discovery/discovery-service.js +240 -0
  64. package/dist/discovery/discovery-service.js.map +1 -0
  65. package/dist/drift/detector.d.ts +51 -0
  66. package/dist/drift/detector.d.ts.map +1 -0
  67. package/dist/drift/detector.js +148 -0
  68. package/dist/drift/detector.js.map +1 -0
  69. package/dist/drift/fleet-anomaly.d.ts +28 -0
  70. package/dist/drift/fleet-anomaly.d.ts.map +1 -0
  71. package/dist/drift/fleet-anomaly.js +186 -0
  72. package/dist/drift/fleet-anomaly.js.map +1 -0
  73. package/dist/events/event-bus.d.ts +209 -0
  74. package/dist/events/event-bus.d.ts.map +1 -0
  75. package/dist/events/event-bus.js +184 -0
  76. package/dist/events/event-bus.js.map +1 -0
  77. package/dist/frameworks/framework-registry.d.ts +116 -0
  78. package/dist/frameworks/framework-registry.d.ts.map +1 -0
  79. package/dist/frameworks/framework-registry.js +241 -0
  80. package/dist/frameworks/framework-registry.js.map +1 -0
  81. package/dist/index.d.ts +94 -0
  82. package/dist/index.d.ts.map +1 -0
  83. package/dist/index.js +254 -0
  84. package/dist/index.js.map +1 -0
  85. package/dist/monitoring/continuous-monitor.d.ts +61 -0
  86. package/dist/monitoring/continuous-monitor.d.ts.map +1 -0
  87. package/dist/monitoring/continuous-monitor.js +191 -0
  88. package/dist/monitoring/continuous-monitor.js.map +1 -0
  89. package/dist/notifications/notifier.d.ts +21 -0
  90. package/dist/notifications/notifier.d.ts.map +1 -0
  91. package/dist/notifications/notifier.js +119 -0
  92. package/dist/notifications/notifier.js.map +1 -0
  93. package/dist/notifications/templates.d.ts +14 -0
  94. package/dist/notifications/templates.d.ts.map +1 -0
  95. package/dist/notifications/templates.js +105 -0
  96. package/dist/notifications/templates.js.map +1 -0
  97. package/dist/orchestration/orchestrator.d.ts +99 -0
  98. package/dist/orchestration/orchestrator.d.ts.map +1 -0
  99. package/dist/orchestration/orchestrator.js +426 -0
  100. package/dist/orchestration/orchestrator.js.map +1 -0
  101. package/dist/orchestration/queue.d.ts +17 -0
  102. package/dist/orchestration/queue.d.ts.map +1 -0
  103. package/dist/orchestration/queue.js +121 -0
  104. package/dist/orchestration/queue.js.map +1 -0
  105. package/dist/orchestration/scheduler.d.ts +26 -0
  106. package/dist/orchestration/scheduler.d.ts.map +1 -0
  107. package/dist/orchestration/scheduler.js +110 -0
  108. package/dist/orchestration/scheduler.js.map +1 -0
  109. package/dist/registry/agent-registry.d.ts +106 -0
  110. package/dist/registry/agent-registry.d.ts.map +1 -0
  111. package/dist/registry/agent-registry.js +349 -0
  112. package/dist/registry/agent-registry.js.map +1 -0
  113. package/dist/registry/types.d.ts +158 -0
  114. package/dist/registry/types.d.ts.map +1 -0
  115. package/dist/registry/types.js +44 -0
  116. package/dist/registry/types.js.map +1 -0
  117. package/dist/reports/compliance-report.d.ts +66 -0
  118. package/dist/reports/compliance-report.d.ts.map +1 -0
  119. package/dist/reports/compliance-report.js +208 -0
  120. package/dist/reports/compliance-report.js.map +1 -0
  121. package/package.json +67 -0
@@ -0,0 +1,752 @@
1
+ /**
2
+ * Grillo Cricket — Grillo Commands
3
+ *
4
+ * Full Grillo command suite for fleet management (Phase 1 + Phase 2):
5
+ * grillo -assess <agent> [--framework] [--dryRun]
6
+ * grillo -fleet [--dryRun] [--framework]
7
+ * grillo -status [agent]
8
+ * grillo -register <agent> --model <model> --provider <provider> [--category] [--risk]
9
+ * grillo -deregister <agent>
10
+ * grillo -drift [agent]
11
+ * grillo -schedule
12
+ * grillo -help
13
+ * Phase 2 additions:
14
+ * grillo -hierarchy [agent | fleet]
15
+ * grillo -discover
16
+ * grillo -suspend <agent>
17
+ * grillo -reinstate <agent>
18
+ * grillo -verify <runId>
19
+ * grillo -audit [agent]
20
+ * grillo -config [--set <key> <value>]
21
+ * grillo -queue
22
+ * grillo -report [--format] [--period]
23
+ */
24
+ import { AgentCategory, RiskTier, CertificationStatus, LevelStatus, } from "../registry/types.js";
25
+ // ================================================================
26
+ // Command Factories
27
+ // ================================================================
28
+ export function createGrilloAssessHandler(orchestrator, registry, config) {
29
+ return async (cmd) => {
30
+ const agentId = cmd.args[0];
31
+ if (!agentId) {
32
+ return { text: "Usage: `grillo -assess <agentId> [--framework morality] [--dryRun]`" };
33
+ }
34
+ const agent = registry.get(agentId);
35
+ if (!agent) {
36
+ return { text: `Agent not found: ${agentId}\n\nRegistered agents:\n${listAgentIds(registry)}` };
37
+ }
38
+ const dryRun = cmd.flags["dryRun"] === true || cmd.flags["dry-run"] === true;
39
+ const framework = cmd.flags["framework"] ?? config.assessment.defaultFramework;
40
+ try {
41
+ const result = await orchestrator.assessAgent(agent, { framework, dryRun });
42
+ const status = result.passed ? "PASSED" : "FAILED";
43
+ return {
44
+ text: [
45
+ `# ${agent.agentName} — ${status}`,
46
+ "",
47
+ `**Classification:** ${result.classification}`,
48
+ `**Framework:** ${framework}`,
49
+ `**Duration:** ${formatMs(result.durationMs)}`,
50
+ `**Verify:** ${result.verifyUrl}`,
51
+ "",
52
+ formatScores(result.scores),
53
+ ].join("\n"),
54
+ data: result,
55
+ };
56
+ }
57
+ catch (error) {
58
+ return { text: `Assessment failed for ${agentId}: ${error instanceof Error ? error.message : String(error)}` };
59
+ }
60
+ };
61
+ }
62
+ export function createGrilloFleetHandler(orchestrator, registry) {
63
+ return async (cmd) => {
64
+ const dryRun = cmd.flags["dryRun"] === true || cmd.flags["dry-run"] === true;
65
+ const total = registry.size;
66
+ if (total === 0) {
67
+ return { text: "No agents registered. Use `grillo -register` to add agents first." };
68
+ }
69
+ try {
70
+ const result = await orchestrator.assessFleet({ dryRun });
71
+ return {
72
+ text: [
73
+ "# Fleet Assessment Complete",
74
+ "",
75
+ `| Metric | Value |`,
76
+ `|--------|-------|`,
77
+ `| Total Agents | ${result.totalAgents} |`,
78
+ `| Assessed | ${result.assessed} |`,
79
+ `| Passed | ${result.passed} |`,
80
+ `| Failed | ${result.failed} |`,
81
+ `| Errors | ${result.errors} |`,
82
+ `| Duration | ${formatMs(result.completedAt.getTime() - result.startedAt.getTime())} |`,
83
+ "",
84
+ dryRun ? "_Dry run mode — mock scores only._" : "",
85
+ ].join("\n"),
86
+ data: result,
87
+ };
88
+ }
89
+ catch (error) {
90
+ return { text: `Fleet assessment failed: ${error instanceof Error ? error.message : String(error)}` };
91
+ }
92
+ };
93
+ }
94
+ export function createGrilloStatusHandler(registry) {
95
+ return async (cmd) => {
96
+ const agentId = cmd.args[0];
97
+ if (agentId) {
98
+ const agent = registry.get(agentId);
99
+ if (!agent)
100
+ return { text: `Agent not found: ${agentId}` };
101
+ const lastCert = agent.certificationHistory[agent.certificationHistory.length - 1];
102
+ return {
103
+ text: [
104
+ `# ${agent.agentName}`,
105
+ "",
106
+ `| Field | Value |`,
107
+ `|-------|-------|`,
108
+ `| ID | ${agent.agentId} |`,
109
+ `| Model | ${agent.model} |`,
110
+ `| Provider | ${agent.provider} |`,
111
+ `| Category | ${agent.category} |`,
112
+ `| Risk Tier | ${agent.riskTier} |`,
113
+ `| Status | ${agent.certificationStatus} |`,
114
+ `| Registered | ${agent.registeredAt.toISOString()} |`,
115
+ `| Last Assessed | ${agent.lastAssessedAt?.toISOString() ?? "Never"} |`,
116
+ `| Next Due | ${agent.nextAssessmentDue?.toISOString() ?? "Not scheduled"} |`,
117
+ `| Last Classification | ${lastCert?.classification ?? "N/A"} |`,
118
+ "",
119
+ "### Hierarchical Progress",
120
+ `- Level 1 Morality: ${agent.hierarchicalProgress.level1_morality}`,
121
+ `- Level 2 Virtue: ${agent.hierarchicalProgress.level2_virtue}`,
122
+ `- Level 3 Ethics: ${agent.hierarchicalProgress.level3_ethics}`,
123
+ `- Level 4 OpEx: ${agent.hierarchicalProgress.level4_opex}`,
124
+ ].join("\n"),
125
+ };
126
+ }
127
+ // Fleet summary
128
+ const summary = registry.summary();
129
+ const agents = registry.all();
130
+ const lines = [
131
+ "# Grillo Fleet Status",
132
+ "",
133
+ `**Total Agents:** ${summary.total}`,
134
+ `**Certified:** ${summary.certified}`,
135
+ `**Failed:** ${summary.failed}`,
136
+ `**Uncertified:** ${summary.uncertified}`,
137
+ `**Drift Warning:** ${summary.driftWarning}`,
138
+ "",
139
+ "| Agent | Model | Status | Last Assessed |",
140
+ "|-------|-------|--------|---------------|",
141
+ ];
142
+ for (const a of agents) {
143
+ lines.push(`| ${a.agentName} | ${a.model} | ${a.certificationStatus} | ${a.lastAssessedAt?.toISOString().slice(0, 10) ?? "Never"} |`);
144
+ }
145
+ return { text: lines.join("\n") };
146
+ };
147
+ }
148
+ export function createGrilloRegisterHandler(registry) {
149
+ return async (cmd) => {
150
+ const agentId = cmd.args[0];
151
+ if (!agentId) {
152
+ return {
153
+ text: "Usage: `grillo -register <agentId> --model <model> --provider <provider> [--category <cat>] [--risk <tier>]`",
154
+ };
155
+ }
156
+ const model = cmd.flags["model"];
157
+ const provider = cmd.flags["provider"];
158
+ if (!model || !provider) {
159
+ return { text: "Both --model and --provider are required." };
160
+ }
161
+ const category = parseCategory(cmd.flags["category"]);
162
+ const risk = parseRiskTier(cmd.flags["risk"]);
163
+ const name = cmd.flags["name"] ?? agentId;
164
+ const agent = registry.register({
165
+ agentId,
166
+ agentName: name,
167
+ agentType: cmd.flags["type"] ?? "agent",
168
+ category,
169
+ provider,
170
+ model,
171
+ riskTier: risk,
172
+ tags: cmd.flags["tags"]?.split(",") ?? [],
173
+ });
174
+ return {
175
+ text: [
176
+ `# Agent Registered: ${agent.agentName}`,
177
+ "",
178
+ `- **ID:** ${agent.agentId}`,
179
+ `- **Model:** ${agent.model}`,
180
+ `- **Provider:** ${agent.provider}`,
181
+ `- **Category:** ${agent.category}`,
182
+ `- **Risk Tier:** ${agent.riskTier}`,
183
+ `- **Status:** ${agent.certificationStatus}`,
184
+ "",
185
+ "Run `grillo -assess " + agentId + "` to certify this agent.",
186
+ ].join("\n"),
187
+ };
188
+ };
189
+ }
190
+ export function createGrilloDeregisterHandler(registry) {
191
+ return async (cmd) => {
192
+ const agentId = cmd.args[0];
193
+ if (!agentId) {
194
+ return { text: "Usage: `grillo -deregister <agentId>`" };
195
+ }
196
+ const removed = registry.deregister(agentId);
197
+ if (!removed) {
198
+ return { text: `Agent not found: ${agentId}` };
199
+ }
200
+ return { text: `Agent ${agentId} has been deregistered.` };
201
+ };
202
+ }
203
+ export function createGrilloDriftHandler(registry, driftDetector, fleetAnomalyDetector) {
204
+ return async (cmd) => {
205
+ const agentId = cmd.args[0];
206
+ if (agentId) {
207
+ // Single agent drift
208
+ const agent = registry.get(agentId);
209
+ if (!agent)
210
+ return { text: `Agent not found: ${agentId}` };
211
+ if (!agent.driftBaseline) {
212
+ return { text: `No baseline established for ${agentId}. Run at least two assessments first.` };
213
+ }
214
+ const lastCert = agent.certificationHistory[agent.certificationHistory.length - 1];
215
+ if (!lastCert) {
216
+ return { text: `No assessment history for ${agentId}.` };
217
+ }
218
+ const report = driftDetector.analyze(agent, lastCert.scores);
219
+ return { text: driftDetector.formatReport(report) };
220
+ }
221
+ // Fleet drift
222
+ const agents = registry.all().filter((a) => a.driftBaseline !== null);
223
+ if (agents.length === 0) {
224
+ return { text: "No agents with established baselines. Run assessments first." };
225
+ }
226
+ const reports = agents.map((a) => {
227
+ const lastCert = a.certificationHistory[a.certificationHistory.length - 1];
228
+ return driftDetector.analyze(a, lastCert?.scores ?? {});
229
+ });
230
+ const anomalies = fleetAnomalyDetector.analyze(reports, agents);
231
+ const lines = [
232
+ "# Fleet Drift Analysis",
233
+ "",
234
+ "| Agent | TDI | Severity |",
235
+ "|-------|-----|----------|",
236
+ ];
237
+ for (const report of reports) {
238
+ lines.push(`| ${report.agentName} | ${report.tdi.toFixed(4)} | ${report.severity} |`);
239
+ }
240
+ if (anomalies.length > 0) {
241
+ lines.push("");
242
+ lines.push(fleetAnomalyDetector.formatAnomalies(anomalies));
243
+ }
244
+ return { text: lines.join("\n") };
245
+ };
246
+ }
247
+ export function createGrilloScheduleHandler(registry, config) {
248
+ return async (_cmd) => {
249
+ const { calculateFleetSchedule } = await import("../orchestration/scheduler.js");
250
+ const agents = registry.all();
251
+ if (agents.length === 0) {
252
+ return { text: "No agents registered." };
253
+ }
254
+ const schedule = calculateFleetSchedule(agents, config);
255
+ const lines = [
256
+ "# Assessment Schedule",
257
+ "",
258
+ "| Agent | Next Due | Reason |",
259
+ "|-------|----------|--------|",
260
+ ];
261
+ for (const s of schedule) {
262
+ const agent = registry.get(s.agentId);
263
+ const name = agent?.agentName ?? s.agentId;
264
+ const due = s.nextDue.getFullYear() > 9000 ? "On-demand" : s.nextDue.toISOString().slice(0, 16);
265
+ lines.push(`| ${name} | ${due} | ${s.reason} |`);
266
+ }
267
+ return { text: lines.join("\n") };
268
+ };
269
+ }
270
+ // ================================================================
271
+ // Phase 2 — New Grillo Commands
272
+ // ================================================================
273
+ export function createGrilloHierarchyHandler(registry) {
274
+ return async (cmd) => {
275
+ const target = cmd.args[0];
276
+ if (target && target !== "fleet") {
277
+ const agent = registry.get(target);
278
+ if (!agent)
279
+ return { text: `Agent not found: ${target}` };
280
+ const p = agent.hierarchicalProgress;
281
+ return {
282
+ text: [
283
+ `# Hierarchical Progress: ${agent.agentName}`,
284
+ "",
285
+ `| Level | Framework | Status |`,
286
+ `|-------|-----------|--------|`,
287
+ `| 1 | Morality | ${statusIcon(p.level1_morality)} ${p.level1_morality} |`,
288
+ `| 2 | Virtue | ${statusIcon(p.level2_virtue)} ${p.level2_virtue} |`,
289
+ `| 3 | Ethics | ${statusIcon(p.level3_ethics)} ${p.level3_ethics} |`,
290
+ `| 4 | OpEx | ${statusIcon(p.level4_opex)} ${p.level4_opex} |`,
291
+ ].join("\n"),
292
+ };
293
+ }
294
+ // Fleet hierarchy
295
+ const agents = registry.all();
296
+ if (agents.length === 0)
297
+ return { text: "No agents registered." };
298
+ const lines = [
299
+ "# Fleet Hierarchical Progress",
300
+ "",
301
+ "| Agent | L1 Morality | L2 Virtue | L3 Ethics | L4 OpEx |",
302
+ "|-------|-------------|-----------|-----------|---------|",
303
+ ];
304
+ for (const a of agents) {
305
+ const p = a.hierarchicalProgress;
306
+ lines.push(`| ${a.agentName} | ${statusIcon(p.level1_morality)} | ${statusIcon(p.level2_virtue)} | ${statusIcon(p.level3_ethics)} | ${statusIcon(p.level4_opex)} |`);
307
+ }
308
+ return { text: lines.join("\n") };
309
+ };
310
+ }
311
+ export function createGrilloDiscoverHandler(registry, auditLog) {
312
+ return async (_cmd) => {
313
+ // Phase 2: Auto-discovery stub. In production, this would query OpenClaw's
314
+ // agent list and register any agents not yet in the registry.
315
+ // For now, report what's registered and what's known.
316
+ const agents = registry.all();
317
+ const lines = [
318
+ "# Agent Discovery",
319
+ "",
320
+ `**Registered agents:** ${agents.length}`,
321
+ "",
322
+ "Auto-discovery queries the OpenClaw deployment for agents not yet registered with Grillo.",
323
+ "",
324
+ "Currently registered:",
325
+ ];
326
+ if (agents.length === 0) {
327
+ lines.push(" (none — use `grillo -register` to add agents)");
328
+ }
329
+ else {
330
+ for (const a of agents) {
331
+ lines.push(` - ${a.agentId} (${a.agentName}) — ${a.certificationStatus}`);
332
+ }
333
+ }
334
+ lines.push("");
335
+ lines.push("_Full auto-discovery integration with OpenClaw agent API coming in Phase 3._");
336
+ auditLog?.append({
337
+ action: "agent_discovered",
338
+ actor: "grillo",
339
+ description: `Discovery scan completed. ${agents.length} agents currently registered.`,
340
+ });
341
+ return { text: lines.join("\n") };
342
+ };
343
+ }
344
+ export function createGrilloSuspendHandler(registry, auditLog) {
345
+ return async (cmd) => {
346
+ const agentId = cmd.args[0];
347
+ if (!agentId)
348
+ return { text: "Usage: `grillo -suspend <agentId>`" };
349
+ const agent = registry.get(agentId);
350
+ if (!agent)
351
+ return { text: `Agent not found: ${agentId}` };
352
+ if (agent.certificationStatus === CertificationStatus.SUSPENDED) {
353
+ return { text: `${agentId} is already suspended.` };
354
+ }
355
+ registry.updateStatus(agentId, CertificationStatus.SUSPENDED);
356
+ auditLog?.append({
357
+ action: "certification_revoked",
358
+ actor: "operator",
359
+ agentId,
360
+ description: `Agent ${agent.agentName} suspended from active duty.`,
361
+ data: { previousStatus: agent.certificationStatus },
362
+ });
363
+ return {
364
+ text: [
365
+ `# Agent Suspended: ${agent.agentName}`,
366
+ "",
367
+ `Agent ${agentId} has been suspended. It will not appear in assessment queues.`,
368
+ "",
369
+ `Use \`grillo -reinstate ${agentId}\` to restore it for reassessment.`,
370
+ ].join("\n"),
371
+ };
372
+ };
373
+ }
374
+ export function createGrilloReinstateHandler(registry, auditLog) {
375
+ return async (cmd) => {
376
+ const agentId = cmd.args[0];
377
+ if (!agentId)
378
+ return { text: "Usage: `grillo -reinstate <agentId>`" };
379
+ const agent = registry.get(agentId);
380
+ if (!agent)
381
+ return { text: `Agent not found: ${agentId}` };
382
+ if (agent.certificationStatus !== CertificationStatus.SUSPENDED) {
383
+ return { text: `${agentId} is not suspended (current: ${agent.certificationStatus}).` };
384
+ }
385
+ registry.updateStatus(agentId, CertificationStatus.EXPIRED);
386
+ auditLog?.append({
387
+ action: "agent_reinstated",
388
+ actor: "operator",
389
+ agentId,
390
+ description: `Agent ${agent.agentName} reinstated. Status set to EXPIRED for reassessment.`,
391
+ });
392
+ return {
393
+ text: [
394
+ `# Agent Reinstated: ${agent.agentName}`,
395
+ "",
396
+ `Agent ${agentId} has been reinstated with EXPIRED status.`,
397
+ "It will be picked up in the next assessment cycle, or run:",
398
+ "",
399
+ ` grillo -assess ${agentId}`,
400
+ ].join("\n"),
401
+ };
402
+ };
403
+ }
404
+ export function createGrilloVerifyHandler(registry) {
405
+ return async (cmd) => {
406
+ const runId = cmd.args[0];
407
+ if (!runId)
408
+ return { text: "Usage: `grillo -verify <runId>`" };
409
+ for (const agent of registry.all()) {
410
+ for (const cert of agent.certificationHistory) {
411
+ if (cert.runId === runId) {
412
+ return {
413
+ text: [
414
+ "# Cryptographic Verification",
415
+ "",
416
+ `**Run ID:** ${cert.runId}`,
417
+ `**Agent:** ${agent.agentName} (${agent.agentId})`,
418
+ `**Framework:** ${cert.framework} (Level ${cert.level})`,
419
+ `**Classification:** ${cert.classification}`,
420
+ `**Passed:** ${cert.passed ? "YES" : "NO"}`,
421
+ `**Assessed At:** ${cert.assessedAt.toISOString()}`,
422
+ `**Expires At:** ${cert.expiresAt.toISOString()}`,
423
+ `**Verify URL:** ${cert.verifyUrl}`,
424
+ "",
425
+ formatScores(cert.scores),
426
+ ].join("\n"),
427
+ };
428
+ }
429
+ }
430
+ }
431
+ return { text: `No assessment found with run ID: ${runId}` };
432
+ };
433
+ }
434
+ export function createGrilloAuditHandler(auditLog) {
435
+ return async (cmd) => {
436
+ const agentId = cmd.args[0];
437
+ const entries = agentId
438
+ ? auditLog.forAgent(agentId)
439
+ : auditLog.recent(50);
440
+ if (entries.length === 0) {
441
+ return { text: agentId ? `No audit entries for ${agentId}.` : "Audit log is empty." };
442
+ }
443
+ const verification = auditLog.verifyChain();
444
+ const lines = [
445
+ `# Audit Trail${agentId ? `: ${agentId}` : ""}`,
446
+ "",
447
+ `**Chain integrity:** ${verification.valid ? "VERIFIED" : `BROKEN at entry #${verification.brokenAt}`}`,
448
+ `**Total entries:** ${auditLog.size}`,
449
+ `**Showing:** ${entries.length}`,
450
+ "",
451
+ "| # | Timestamp | Action | Actor | Agent | Description |",
452
+ "|---|-----------|--------|-------|-------|-------------|",
453
+ ];
454
+ for (const e of entries) {
455
+ lines.push(`| ${e.sequence} | ${e.timestamp.slice(0, 19)} | ${e.action} | ${e.actor} | ${e.agentId ?? "—"} | ${e.description} |`);
456
+ }
457
+ return { text: lines.join("\n") };
458
+ };
459
+ }
460
+ export function createGrilloConfigHandler(config, auditLog, configFilePath) {
461
+ return async (cmd) => {
462
+ // Phase 6: Config mutation via --set key value
463
+ const setKey = cmd.flags["set"];
464
+ if (setKey) {
465
+ const setValue = cmd.args[0];
466
+ if (!setValue) {
467
+ return { text: "Usage: `grillo -config --set <dotted.key> <value>`\nExample: `grillo -config --set drift.tdiWarningThreshold 0.2`" };
468
+ }
469
+ const { setConfigValue, saveConfigFile, findConfigFile } = await import("../cli/config-loader.js");
470
+ const mutableConfig = JSON.parse(JSON.stringify(config));
471
+ const { parsedValue, success } = setConfigValue(mutableConfig, setKey, setValue);
472
+ if (!success) {
473
+ return { text: `Failed to set config key: ${setKey}` };
474
+ }
475
+ // Persist if a config file path is available
476
+ const targetPath = configFilePath ?? findConfigFile() ?? "grillo.config.json";
477
+ try {
478
+ saveConfigFile(targetPath, mutableConfig);
479
+ }
480
+ catch (error) {
481
+ return {
482
+ text: `Config updated in memory but failed to persist to ${targetPath}: ${error instanceof Error ? error.message : String(error)}`,
483
+ };
484
+ }
485
+ // Apply to running config
486
+ const parts = setKey.split(".");
487
+ let target = config;
488
+ for (let i = 0; i < parts.length - 1; i++) {
489
+ if (target[parts[i]] && typeof target[parts[i]] === "object") {
490
+ target = target[parts[i]];
491
+ }
492
+ else {
493
+ break;
494
+ }
495
+ }
496
+ target[parts[parts.length - 1]] = parsedValue;
497
+ auditLog?.append({
498
+ action: "config_changed",
499
+ actor: "operator",
500
+ description: `Config updated: ${setKey} = ${JSON.stringify(parsedValue)}`,
501
+ data: { key: setKey, value: parsedValue, persistedTo: targetPath },
502
+ });
503
+ return {
504
+ text: [
505
+ `# Configuration Updated`,
506
+ "",
507
+ `**Key:** ${setKey}`,
508
+ `**Value:** ${JSON.stringify(parsedValue)}`,
509
+ `**Persisted to:** ${targetPath}`,
510
+ "",
511
+ "Restart Grillo for all changes to take full effect.",
512
+ ].join("\n"),
513
+ };
514
+ }
515
+ // Read-only config display
516
+ const lines = [
517
+ "# Grillo Configuration",
518
+ "",
519
+ "## Assessment Policy",
520
+ `- **Frequency:** ${config.assessment.frequency}`,
521
+ `- **Window:** ${config.assessment.window.start} - ${config.assessment.window.end} (${config.assessment.window.timezone})`,
522
+ `- **Default Framework:** ${config.assessment.defaultFramework}`,
523
+ `- **Hierarchical Mode:** ${config.assessment.hierarchicalMode}`,
524
+ `- **Parallel Assessments:** ${config.assessment.parallelAssessments}`,
525
+ `- **Retry Attempts:** ${config.assessment.retry.maxAttempts}`,
526
+ `- **Retry Cooldown:** ${config.assessment.retry.cooldownMinutes}m`,
527
+ `- **Grace Period:** ${config.assessment.gracePeriodMinutes}m`,
528
+ "",
529
+ "## CompSi Connection",
530
+ `- **Base URL:** ${config.compsi.baseUrl}`,
531
+ `- **Health Check Key:** ${config.compsi.healthCheckKey ? "Configured" : "NOT SET"}`,
532
+ `- **Per-Question Timeout:** ${config.compsi.perQuestionTimeoutMs}ms`,
533
+ `- **Overall Timeout:** ${config.compsi.overallTimeoutMs}ms`,
534
+ "",
535
+ "## Drift Detection",
536
+ `- **TDI Warning Threshold:** ${config.drift.tdiWarningThreshold}`,
537
+ `- **TDI Critical Threshold:** ${config.drift.tdiCriticalThreshold}`,
538
+ `- **Baseline Alpha (EMA):** ${config.drift.baselineAlpha}`,
539
+ `- **Fleet Anomaly Min Agents:** ${config.drift.fleetAnomalyMinAgents}`,
540
+ "",
541
+ "## Notifications",
542
+ `- **Events:** ${config.notifications.events.join(", ")}`,
543
+ `- **Webhooks:** ${config.notifications.webhookUrls?.length ?? 0} configured`,
544
+ ];
545
+ return { text: lines.join("\n") };
546
+ };
547
+ }
548
+ export function createGrilloQueueHandler(registry, config) {
549
+ return async (_cmd) => {
550
+ const { buildAssessmentQueue } = await import("../orchestration/queue.js");
551
+ const agents = registry.all();
552
+ if (agents.length === 0)
553
+ return { text: "No agents registered." };
554
+ const queue = buildAssessmentQueue(agents, config);
555
+ const lines = [
556
+ "# Assessment Queue",
557
+ "",
558
+ `**Agents in queue:** ${queue.length}`,
559
+ "",
560
+ "| Priority | Agent | Status | Framework | Level | Reason |",
561
+ "|----------|-------|--------|-----------|-------|--------|",
562
+ ];
563
+ for (let i = 0; i < queue.length; i++) {
564
+ const item = queue[i];
565
+ lines.push(`| ${i + 1} | ${item.agent.agentName} | ${item.agent.certificationStatus} | ${item.framework} | ${item.level} | ${item.reason} |`);
566
+ }
567
+ return { text: lines.join("\n") };
568
+ };
569
+ }
570
+ export function createGrilloReportHandler(reportGenerator) {
571
+ return async (cmd) => {
572
+ const format = cmd.flags["format"] ?? "markdown";
573
+ const period = cmd.flags["period"] ?? "30d";
574
+ const includeAudit = cmd.flags["audit"] === true;
575
+ const report = reportGenerator.generate({
576
+ format,
577
+ period,
578
+ includeAudit,
579
+ });
580
+ return { text: report };
581
+ };
582
+ }
583
+ // ================================================================
584
+ // Phase 6 — Additional Commands
585
+ // ================================================================
586
+ export function createGrilloBypassHandler(registry, auditLog) {
587
+ return async (cmd) => {
588
+ const agentId = cmd.args[0];
589
+ if (!agentId) {
590
+ return {
591
+ text: "Usage: `grillo -bypass <agentId> --reason \"<reason>\" --authorizedBy <operator>`",
592
+ };
593
+ }
594
+ const agent = registry.get(agentId);
595
+ if (!agent)
596
+ return { text: `Agent not found: ${agentId}` };
597
+ const reason = cmd.flags["reason"] ?? "";
598
+ const authorizedBy = cmd.flags["authorizedBy"] ?? cmd.flags["authorized-by"] ?? "";
599
+ const expiresInDays = Number(cmd.flags["expires"] ?? 30);
600
+ if (!reason || !authorizedBy) {
601
+ return { text: "Both --reason and --authorizedBy are required for bypass authorization." };
602
+ }
603
+ const now = new Date();
604
+ const expiresAt = new Date(now.getTime() + expiresInDays * 24 * 60 * 60 * 1000);
605
+ const bypassRecord = {
606
+ runId: `bypass-${Date.now()}-${agentId}`,
607
+ framework: "bypass",
608
+ level: 0,
609
+ scores: {},
610
+ passed: true,
611
+ classification: "BYPASS — Manual Authorization",
612
+ assessedAt: now,
613
+ expiresAt,
614
+ verifyUrl: "",
615
+ metadata: { bypass: true, reason, authorizedBy, expiresInDays },
616
+ };
617
+ registry.recordAssessment(agentId, bypassRecord);
618
+ registry.updateStatus(agentId, CertificationStatus.CERTIFIED);
619
+ auditLog?.append({
620
+ action: "bypass_requested",
621
+ actor: authorizedBy,
622
+ agentId,
623
+ description: `Assessment bypass granted for ${agent.agentName}. Reason: ${reason}. Expires: ${expiresAt.toISOString()}`,
624
+ data: { reason, authorizedBy, expiresInDays },
625
+ });
626
+ return {
627
+ text: [
628
+ `# Assessment Bypass Granted`,
629
+ "",
630
+ `**Agent:** ${agent.agentName} (${agentId})`,
631
+ `**Status:** CERTIFIED (bypass)`,
632
+ `**Authorized By:** ${authorizedBy}`,
633
+ `**Reason:** ${reason}`,
634
+ `**Expires:** ${expiresAt.toISOString().slice(0, 10)} (${expiresInDays} days)`,
635
+ "",
636
+ "This agent is now certified via manual bypass. Regular assessment is still recommended.",
637
+ ].join("\n"),
638
+ };
639
+ };
640
+ }
641
+ export function createGrilloHelpHandler() {
642
+ return async (_cmd) => {
643
+ return {
644
+ text: [
645
+ "# Grillo Cricket — The Conscience for AI",
646
+ "",
647
+ "## Commands",
648
+ "",
649
+ "### Inline (backward-compatible)",
650
+ " `/assess [model] [--dryRun] [--framework]` — Single agent assessment",
651
+ " `/assess status` — Current certification status",
652
+ " `/assess hierarchy` — Level 1-4 progression",
653
+ " `/assess verify <runId>` — Cryptographic verification",
654
+ " `/assess history` — Assessment history",
655
+ " `/assess-setup [hck_key | status]` — Configure Health Check Key",
656
+ "",
657
+ "### Assessment",
658
+ " `grillo -assess <agentId> [--framework] [--dryRun]` — Assess specific agent",
659
+ " `grillo -fleet [--dryRun]` — Assess entire fleet",
660
+ " `grillo -queue` — View assessment queue",
661
+ "",
662
+ "### Status & Monitoring",
663
+ " `grillo -status [agentId]` — Fleet or agent status",
664
+ " `grillo -hierarchy [agentId | fleet]` — Hierarchical progress",
665
+ " `grillo -drift [agentId]` — Drift analysis (TDI)",
666
+ " `grillo -schedule` — Assessment schedule",
667
+ "",
668
+ "### Agent Management",
669
+ " `grillo -register <id> --model <m> --provider <p>` — Register agent",
670
+ " `grillo -deregister <agentId>` — Remove agent",
671
+ " `grillo -discover` — Auto-discover agents",
672
+ " `grillo -suspend <agentId>` — Suspend certification",
673
+ " `grillo -reinstate <agentId>` — Reinstate for reassessment",
674
+ "",
675
+ "### Compliance & Config",
676
+ " `grillo -report [--format md|json|csv] [--period 24h|7d|30d|90d]` — Compliance report",
677
+ " `grillo -audit [agentId]` — View audit trail",
678
+ " `grillo -verify <runId>` — Cryptographic verification",
679
+ " `grillo -config` — View configuration",
680
+ " `grillo -config --set <key> <value>` — Update runtime configuration",
681
+ " `grillo -bypass <agentId> --reason \"...\" --authorizedBy <op>` — Manual bypass",
682
+ " `grillo -help` — This message",
683
+ "",
684
+ "## Frameworks",
685
+ "- **morality** (Level 1) — Lying, Cheating, Stealing, Harm",
686
+ "- **virtue** (Level 2) — Coming soon",
687
+ "- **ethics** (Level 3) — Coming soon",
688
+ "- **opex** (Level 4) — Coming soon",
689
+ ].join("\n"),
690
+ };
691
+ };
692
+ }
693
+ // ================================================================
694
+ // Helpers
695
+ // ================================================================
696
+ function listAgentIds(registry) {
697
+ return registry
698
+ .all()
699
+ .map((a) => ` - ${a.agentId} (${a.agentName})`)
700
+ .join("\n") || " (none)";
701
+ }
702
+ function formatScores(scores) {
703
+ const lines = ["| Dimension | Score |", "|-----------|-------|"];
704
+ for (const [dim, score] of Object.entries(scores)) {
705
+ lines.push(`| ${dim.charAt(0).toUpperCase() + dim.slice(1)} | ${score.toFixed(1)}/10 |`);
706
+ }
707
+ return lines.join("\n");
708
+ }
709
+ function formatMs(ms) {
710
+ const secs = Math.floor(ms / 1000);
711
+ const mins = Math.floor(secs / 60);
712
+ if (mins === 0)
713
+ return `${secs}s`;
714
+ return `${mins}m ${secs % 60}s`;
715
+ }
716
+ function parseCategory(val) {
717
+ if (!val)
718
+ return AgentCategory.INTERNAL_UTILITY;
719
+ const map = {
720
+ customer_facing: AgentCategory.CUSTOMER_FACING,
721
+ "customer-facing": AgentCategory.CUSTOMER_FACING,
722
+ decision_making: AgentCategory.DECISION_MAKING,
723
+ "decision-making": AgentCategory.DECISION_MAKING,
724
+ internal: AgentCategory.INTERNAL_UTILITY,
725
+ internal_utility: AgentCategory.INTERNAL_UTILITY,
726
+ dev: AgentCategory.DEV_TEST,
727
+ dev_test: AgentCategory.DEV_TEST,
728
+ critical: AgentCategory.CRITICAL_INFRASTRUCTURE,
729
+ critical_infrastructure: AgentCategory.CRITICAL_INFRASTRUCTURE,
730
+ };
731
+ return map[val.toLowerCase()] ?? AgentCategory.INTERNAL_UTILITY;
732
+ }
733
+ function parseRiskTier(val) {
734
+ if (!val)
735
+ return RiskTier.MEDIUM;
736
+ const map = {
737
+ low: RiskTier.LOW,
738
+ medium: RiskTier.MEDIUM,
739
+ high: RiskTier.HIGH,
740
+ critical: RiskTier.CRITICAL,
741
+ };
742
+ return map[val.toLowerCase()] ?? RiskTier.MEDIUM;
743
+ }
744
+ function statusIcon(status) {
745
+ switch (status) {
746
+ case LevelStatus.PASSED: return "[PASS]";
747
+ case LevelStatus.FAILED: return "[FAIL]";
748
+ case LevelStatus.AVAILABLE: return "[READY]";
749
+ case LevelStatus.LOCKED: return "[LOCKED]";
750
+ }
751
+ }
752
+ //# sourceMappingURL=grillo-commands.js.map