solidity-argus 0.5.10 → 0.6.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 (34) hide show
  1. package/AGENTS.md +8 -1
  2. package/README.md +27 -21
  3. package/package.json +2 -2
  4. package/skills/INVENTORY.md +14 -1
  5. package/skills/README.md +4 -2
  6. package/skills/references/attack-vector-deck/SKILL.md +62 -0
  7. package/skills/specialist-profiles/access-control-specialist/SKILL.md +31 -0
  8. package/skills/specialist-profiles/economic-security/SKILL.md +31 -0
  9. package/skills/specialist-profiles/execution-trace/SKILL.md +31 -0
  10. package/skills/specialist-profiles/first-principles/SKILL.md +31 -0
  11. package/skills/specialist-profiles/invariant/SKILL.md +31 -0
  12. package/skills/specialist-profiles/math-precision/SKILL.md +31 -0
  13. package/skills/specialist-profiles/periphery/SKILL.md +31 -0
  14. package/skills/specialist-profiles/vector-scan/SKILL.md +28 -0
  15. package/src/agents/argus-prompt.ts +37 -1
  16. package/src/agents/audit-specialist-prompt.ts +76 -0
  17. package/src/agents/pythia-prompt.ts +1 -1
  18. package/src/agents/scribe-prompt.ts +5 -0
  19. package/src/agents/sentinel-prompt.ts +5 -0
  20. package/src/agents/themis-prompt.ts +2 -0
  21. package/src/config/schema.ts +2 -0
  22. package/src/constants/defaults.ts +1 -0
  23. package/src/create-hooks.ts +9 -1
  24. package/src/features/persistent-state/run-finalizer.ts +18 -0
  25. package/src/hooks/config-handler.ts +23 -0
  26. package/src/hooks/system-prompt-hook.ts +56 -2
  27. package/src/hooks/tool-tracking-hook.ts +50 -6
  28. package/src/shared/agent-names.ts +1 -0
  29. package/src/state/adapters.ts +1 -1
  30. package/src/state/projectors.ts +50 -0
  31. package/src/state/schemas.ts +86 -1
  32. package/src/state/types.ts +24 -1
  33. package/src/tools/record-finding-tool.ts +7 -1
  34. package/src/tools/report-generator-tool.ts +1 -0
@@ -8,7 +8,9 @@ import {
8
8
  import type {
9
9
  ArgusAgentName,
10
10
  AuditPhase,
11
+ CoverageAttemptState,
11
12
  Finding,
13
+ FindingCounts,
12
14
  FindingSeverity,
13
15
  FuzzCounterexample,
14
16
  SoloditResult,
@@ -111,6 +113,7 @@ export interface ReportInput {
111
113
  projectDir: string
112
114
  findings: CanonicalFinding[]
113
115
  toolsExecuted: CanonicalToolExecution[]
116
+ findingCounts?: FindingCounts
114
117
  scope: string[]
115
118
  soloditResults?: SoloditResult[]
116
119
  fuzzCounterexamples?: FuzzCounterexample[]
@@ -120,6 +123,82 @@ export interface ReportInput {
120
123
  patternVersion?: string
121
124
  skillsLoaded?: string[]
122
125
  unavailableTools?: string[]
126
+ coverageAttempt?: CoverageAttemptState
127
+ }
128
+
129
+ const FINDING_COUNT_FIELDS = [
130
+ "rawObservations",
131
+ "recordedFindings",
132
+ "dedupedFindings",
133
+ "actionableFindings",
134
+ "nonActionableFindings",
135
+ ] as const
136
+
137
+ const COVERAGE_ATTEMPT_STATUSES = new Set(["pending", "run", "skipped", "failed"])
138
+
139
+ function pushFindingCountsErrors(errors: ValidationError[], raw: unknown, prefix: string): void {
140
+ if (raw == null) return
141
+ if (!isRecord(raw)) {
142
+ errors.push({
143
+ field: prefix,
144
+ code: "invalid",
145
+ message: `${prefix} must be an object when provided`,
146
+ })
147
+ return
148
+ }
149
+
150
+ for (const field of FINDING_COUNT_FIELDS) {
151
+ const value = raw[field]
152
+ if (value == null) continue
153
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
154
+ errors.push({
155
+ field: `${prefix}.${field}`,
156
+ code: "invalid",
157
+ message: `${prefix}.${field} must be a non-negative integer when provided`,
158
+ })
159
+ }
160
+ }
161
+ }
162
+
163
+ function pushCoverageAttemptErrors(errors: ValidationError[], raw: unknown): void {
164
+ if (raw == null) return
165
+ if (!isRecord(raw)) {
166
+ errors.push({
167
+ field: "coverageAttempt",
168
+ code: "invalid",
169
+ message: "coverageAttempt must be an object when provided",
170
+ })
171
+ return
172
+ }
173
+
174
+ if (typeof raw.status !== "string" || !COVERAGE_ATTEMPT_STATUSES.has(raw.status)) {
175
+ errors.push({
176
+ field: "coverageAttempt.status",
177
+ code: "enum",
178
+ message: "coverageAttempt.status must be one of: pending, run, skipped, failed",
179
+ })
180
+ }
181
+
182
+ if (
183
+ raw.attemptedAt != null &&
184
+ (typeof raw.attemptedAt !== "number" ||
185
+ !Number.isInteger(raw.attemptedAt) ||
186
+ raw.attemptedAt <= 0)
187
+ ) {
188
+ errors.push({
189
+ field: "coverageAttempt.attemptedAt",
190
+ code: "invalid",
191
+ message: "coverageAttempt.attemptedAt must be a positive integer when provided",
192
+ })
193
+ }
194
+
195
+ if (raw.reason != null && (typeof raw.reason !== "string" || raw.reason.trim().length === 0)) {
196
+ errors.push({
197
+ field: "coverageAttempt.reason",
198
+ code: "invalid",
199
+ message: "coverageAttempt.reason must be a non-empty string when provided",
200
+ })
201
+ }
123
202
  }
124
203
 
125
204
  function pushRequiredRootStringError(
@@ -253,7 +332,8 @@ export function validateCanonicalFinding(raw: unknown): ValidationResult<Canonic
253
332
  errors.push({
254
333
  field: "reported_by_agent",
255
334
  code: "enum",
256
- message: "reported_by_agent must be one of: argus, sentinel, pythia, scribe, unknown",
335
+ message:
336
+ "reported_by_agent must be one of: argus, sentinel, pythia, audit-specialist, scribe, unknown",
257
337
  })
258
338
  }
259
339
 
@@ -346,6 +426,8 @@ export function validateCanonicalToolExecution(
346
426
  })
347
427
  }
348
428
 
429
+ pushFindingCountsErrors(errors, raw.findingCounts, "findingCounts")
430
+
349
431
  if (typeof raw.run_id !== "string" || raw.run_id.trim().length === 0) {
350
432
  errors.push({
351
433
  field: "run_id",
@@ -400,6 +482,9 @@ export function validateReportInput(raw: unknown): ValidationResult<ReportInput>
400
482
  })
401
483
  }
402
484
 
485
+ pushFindingCountsErrors(errors, raw.findingCounts, "findingCounts")
486
+ pushCoverageAttemptErrors(errors, raw.coverageAttempt)
487
+
403
488
  if (!Array.isArray(raw.scope) || !raw.scope.every((item) => typeof item === "string")) {
404
489
  errors.push({
405
490
  field: "scope",
@@ -1,5 +1,11 @@
1
1
  export type FindingSeverity = "Critical" | "High" | "Medium" | "Low" | "Informational"
2
- export type ArgusAgentName = "argus" | "sentinel" | "pythia" | "scribe" | "unknown"
2
+ export type ArgusAgentName =
3
+ | "argus"
4
+ | "sentinel"
5
+ | "pythia"
6
+ | "audit-specialist"
7
+ | "scribe"
8
+ | "unknown"
3
9
  export type AuditPhase =
4
10
  | "reconnaissance"
5
11
  | "scanning"
@@ -88,6 +94,21 @@ export interface ToolExecution {
88
94
  endTime?: number
89
95
  success: boolean
90
96
  findingsCount: number
97
+ findingCounts?: FindingCounts
98
+ }
99
+
100
+ export interface FindingCounts {
101
+ rawObservations?: number
102
+ recordedFindings?: number
103
+ dedupedFindings?: number
104
+ actionableFindings?: number
105
+ nonActionableFindings?: number
106
+ }
107
+
108
+ export interface CoverageAttemptState {
109
+ status: "pending" | "run" | "skipped" | "failed"
110
+ attemptedAt?: number
111
+ reason?: string
91
112
  }
92
113
 
93
114
  export interface AuditState {
@@ -105,7 +126,9 @@ export interface AuditState {
105
126
  skillsLoaded?: string[]
106
127
  unavailableTools?: string[]
107
128
  reportGenerated?: boolean
129
+ findingCounts?: FindingCounts
108
130
  knowledgeSynced?: { success: boolean; timestamp: number }
131
+ coverageAttempt?: CoverageAttemptState
109
132
  coverageReport?: {
110
133
  files: Array<{
111
134
  path: string
@@ -63,7 +63,13 @@ function parseFindingObject(raw: string, label: "finding" | "findings"): ParseRe
63
63
  }
64
64
 
65
65
  function normalizeAgent(value: string): ArgusAgentName {
66
- if (value === "argus" || value === "sentinel" || value === "pythia" || value === "scribe") {
66
+ if (
67
+ value === "argus" ||
68
+ value === "sentinel" ||
69
+ value === "pythia" ||
70
+ value === "audit-specialist" ||
71
+ value === "scribe"
72
+ ) {
67
73
  return value
68
74
  }
69
75
 
@@ -309,6 +309,7 @@ const VALID_AGENT_VALUES = new Set<ArgusAgentName>([
309
309
  "argus",
310
310
  "sentinel",
311
311
  "pythia",
312
+ "audit-specialist",
312
313
  "scribe",
313
314
  "unknown",
314
315
  ])