solidity-argus 0.2.0 → 0.3.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 (167) hide show
  1. package/AGENTS.md +3 -3
  2. package/README.md +93 -37
  3. package/package.json +33 -7
  4. package/skills/INVENTORY.md +88 -57
  5. package/skills/README.md +26 -23
  6. package/skills/case-studies/beanstalk-governance/SKILL.md +52 -0
  7. package/skills/case-studies/bzx-flash-loan/SKILL.md +53 -0
  8. package/skills/case-studies/cream-finance/SKILL.md +52 -0
  9. package/skills/case-studies/curve-reentrancy/SKILL.md +52 -0
  10. package/skills/case-studies/dao-hack/SKILL.md +51 -0
  11. package/skills/case-studies/euler-finance/SKILL.md +52 -0
  12. package/skills/case-studies/harvest-finance/SKILL.md +52 -0
  13. package/skills/case-studies/level-finance/SKILL.md +51 -0
  14. package/skills/case-studies/mango-markets/SKILL.md +53 -0
  15. package/skills/case-studies/nomad-bridge/SKILL.md +51 -0
  16. package/skills/case-studies/parity-multisig/SKILL.md +55 -0
  17. package/skills/case-studies/poly-network/SKILL.md +51 -0
  18. package/skills/case-studies/rari-fuse/SKILL.md +51 -0
  19. package/skills/case-studies/ronin-bridge/SKILL.md +52 -0
  20. package/skills/case-studies/wormhole-bridge/SKILL.md +51 -0
  21. package/skills/manifests/smartbugs.json +1 -3
  22. package/skills/manifests/sunweb3sec.json +1 -3
  23. package/skills/vulnerability-patterns/access-control/SKILL.md +14 -0
  24. package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +13 -1
  25. package/skills/vulnerability-patterns/assert-violation/SKILL.md +8 -1
  26. package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +12 -1
  27. package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +2 -1
  28. package/skills/vulnerability-patterns/cross-chain-bridge-vulnerabilities/SKILL.md +217 -0
  29. package/skills/vulnerability-patterns/default-visibility/SKILL.md +13 -1
  30. package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +2 -1
  31. package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +8 -1
  32. package/skills/vulnerability-patterns/dos-revert/SKILL.md +1 -0
  33. package/skills/vulnerability-patterns/erc4626-exchange-rate-manipulation/SKILL.md +64 -0
  34. package/skills/vulnerability-patterns/fee-on-transfer-tokens/SKILL.md +93 -0
  35. package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +1 -0
  36. package/skills/vulnerability-patterns/floating-pragma/SKILL.md +8 -1
  37. package/skills/vulnerability-patterns/front-running-attacks/SKILL.md +209 -0
  38. package/skills/vulnerability-patterns/gas-optimization-patterns/SKILL.md +203 -0
  39. package/skills/vulnerability-patterns/governance-attacks/SKILL.md +208 -0
  40. package/skills/vulnerability-patterns/hash-collision/SKILL.md +8 -1
  41. package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +12 -1
  42. package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +8 -1
  43. package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +8 -1
  44. package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +12 -1
  45. package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +7 -1
  46. package/skills/vulnerability-patterns/logic-errors/SKILL.md +10 -0
  47. package/skills/vulnerability-patterns/missing-parameter-bounds/SKILL.md +44 -0
  48. package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +17 -1
  49. package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +12 -1
  50. package/skills/vulnerability-patterns/off-by-one/SKILL.md +7 -1
  51. package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +9 -0
  52. package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +8 -1
  53. package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +1 -0
  54. package/skills/vulnerability-patterns/proxy-vulnerabilities/SKILL.md +209 -0
  55. package/skills/vulnerability-patterns/reentrancy/SKILL.md +9 -0
  56. package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +8 -1
  57. package/skills/vulnerability-patterns/share-accounting-desynchronization/SKILL.md +44 -0
  58. package/skills/vulnerability-patterns/signature-malleability/SKILL.md +2 -1
  59. package/skills/vulnerability-patterns/stateful-parameter-update-drift/SKILL.md +44 -0
  60. package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +12 -1
  61. package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +2 -1
  62. package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +8 -1
  63. package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +8 -1
  64. package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +8 -1
  65. package/skills/vulnerability-patterns/unsafe-erc20-transfers/SKILL.md +132 -0
  66. package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +12 -1
  67. package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +12 -1
  68. package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +11 -1
  69. package/skills/vulnerability-patterns/unused-variables/SKILL.md +8 -1
  70. package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +8 -1
  71. package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +8 -1
  72. package/skills/vulnerability-patterns/weird-tokens/SKILL.md +10 -0
  73. package/skills/vulnerability-patterns/zero-address-misconfiguration/SKILL.md +48 -0
  74. package/src/agents/argus-prompt.ts +24 -7
  75. package/src/agents/pythia-prompt.ts +3 -4
  76. package/src/agents/scribe-prompt.ts +7 -2
  77. package/src/agents/sentinel-prompt.ts +32 -3
  78. package/src/cli/cli-program.ts +29 -26
  79. package/src/cli/commands/check-skills.ts +135 -0
  80. package/src/cli/commands/doctor.ts +48 -26
  81. package/src/cli/commands/init.ts +5 -3
  82. package/src/cli/commands/install.ts +7 -5
  83. package/src/cli/commands/lint-skills.ts +16 -12
  84. package/src/cli/index.ts +5 -5
  85. package/src/cli/types.ts +3 -3
  86. package/src/config/index.ts +1 -1
  87. package/src/config/loader.ts +4 -6
  88. package/src/config/schema.ts +4 -5
  89. package/src/config/types.ts +2 -2
  90. package/src/constants/defaults.ts +2 -0
  91. package/src/create-hooks.ts +145 -34
  92. package/src/create-managers.ts +10 -8
  93. package/src/create-tools.ts +13 -9
  94. package/src/features/background-agent/background-manager.ts +93 -87
  95. package/src/features/background-agent/index.ts +1 -1
  96. package/src/features/context-monitor/context-monitor.ts +3 -3
  97. package/src/features/context-monitor/index.ts +2 -2
  98. package/src/features/error-recovery/session-recovery.ts +2 -4
  99. package/src/features/error-recovery/tool-error-recovery.ts +12 -7
  100. package/src/features/index.ts +5 -5
  101. package/src/features/persistent-state/audit-state-manager.ts +143 -60
  102. package/src/features/persistent-state/global-run-index.ts +38 -0
  103. package/src/features/persistent-state/index.ts +1 -1
  104. package/src/features/persistent-state/run-journal.ts +86 -0
  105. package/src/hooks/config-handler.ts +28 -11
  106. package/src/hooks/context-budget.ts +2 -5
  107. package/src/hooks/event-hook.ts +47 -23
  108. package/src/hooks/hook-system.ts +4 -4
  109. package/src/hooks/index.ts +5 -5
  110. package/src/hooks/knowledge-sync-hook.ts +18 -21
  111. package/src/hooks/recon-context-builder.ts +2 -2
  112. package/src/hooks/safe-create-hook.ts +6 -7
  113. package/src/hooks/tool-tracking-hook.ts +104 -50
  114. package/src/hooks/types.ts +2 -1
  115. package/src/index.ts +23 -36
  116. package/src/knowledge/retry.ts +22 -22
  117. package/src/knowledge/scvd-client.ts +88 -95
  118. package/src/knowledge/scvd-errors.ts +35 -35
  119. package/src/knowledge/scvd-index.ts +78 -80
  120. package/src/knowledge/scvd-sync.ts +106 -101
  121. package/src/managers/index.ts +1 -1
  122. package/src/managers/types.ts +19 -14
  123. package/src/plugin-interface.ts +7 -9
  124. package/src/shared/binary-utils.ts +44 -35
  125. package/src/shared/deep-merge.ts +55 -36
  126. package/src/shared/file-utils.ts +21 -19
  127. package/src/shared/index.ts +11 -5
  128. package/src/shared/jsonc-parser.ts +123 -28
  129. package/src/shared/logger.ts +16 -3
  130. package/src/shared/project-utils.ts +30 -0
  131. package/src/skills/analysis/cluster.ts +414 -0
  132. package/src/skills/analysis/gates.ts +227 -0
  133. package/src/skills/analysis/index.ts +33 -0
  134. package/src/skills/analysis/normalize.ts +217 -0
  135. package/src/skills/analysis/similarity.ts +224 -0
  136. package/src/skills/argus-skill-resolver.ts +17 -6
  137. package/src/skills/skill-schema.ts +11 -10
  138. package/src/solodit-lifecycle.ts +202 -0
  139. package/src/state/audit-state.ts +8 -8
  140. package/src/state/finding-store.ts +68 -55
  141. package/src/state/types.ts +88 -67
  142. package/src/tools/argus-skill-load-tool.ts +12 -7
  143. package/src/tools/contract-analyzer-tool.ts +60 -77
  144. package/src/tools/forge-coverage-tool.ts +226 -0
  145. package/src/tools/forge-fuzz-tool.ts +127 -127
  146. package/src/tools/forge-test-tool.ts +153 -157
  147. package/src/tools/gas-analysis-tool.ts +264 -0
  148. package/src/tools/pattern-checker-tool.ts +185 -190
  149. package/src/tools/pattern-loader.ts +5 -111
  150. package/src/tools/proxy-detection-tool.ts +224 -0
  151. package/src/tools/report-generator-tool.ts +268 -200
  152. package/src/tools/slither-tool.ts +266 -218
  153. package/src/tools/solodit-search-tool.ts +216 -119
  154. package/src/tools/sync-knowledge-tool.ts +7 -11
  155. package/src/utils/audit-artifact-detector.ts +28 -29
  156. package/src/utils/dependency-scanner.ts +37 -37
  157. package/src/utils/project-detector.ts +111 -124
  158. package/src/utils/solidity-parser.ts +103 -74
  159. package/skills/patterns/access-control.yaml +0 -31
  160. package/skills/patterns/erc4626.yaml +0 -29
  161. package/skills/patterns/flash-loan.yaml +0 -20
  162. package/skills/patterns/oracle.yaml +0 -30
  163. package/skills/patterns/proxy.yaml +0 -30
  164. package/skills/patterns/reentrancy.yaml +0 -30
  165. package/skills/patterns/signature.yaml +0 -31
  166. package/src/hooks/event-hook-v2.ts +0 -99
  167. package/src/state/plugin-state.ts +0 -14
@@ -1,122 +1,125 @@
1
- import type { BackgroundManager } from "../../managers/types";
2
- import { createLogger } from "../../shared/logger";
1
+ import type { BackgroundManager } from "../../managers/types"
2
+ import { createLogger } from "../../shared/logger"
3
3
 
4
- type TaskStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
5
- type CompletionCallback = (taskId: string, result: unknown) => void;
4
+ type TaskStatus = "queued" | "running" | "completed" | "failed" | "cancelled"
5
+ type CompletionCallback = (taskId: string, result: unknown) => void
6
6
 
7
7
  export interface BackgroundTaskOptions {
8
- priority?: number;
9
- max_concurrent?: number;
8
+ priority?: number
9
+ max_concurrent?: number
10
10
  }
11
11
 
12
12
  export interface BackgroundManagerWithTaskCallbacks extends BackgroundManager {
13
- dispatch(agentName: string, prompt: string, options?: BackgroundTaskOptions): string;
14
- onComplete(callback: CompletionCallback): void;
15
- onComplete(taskId: string, callback: CompletionCallback): void;
13
+ dispatch(agentName: string, prompt: string, options?: BackgroundTaskOptions): string
14
+ onComplete(callback: CompletionCallback): void
15
+ onComplete(taskId: string, callback: CompletionCallback): void
16
16
  }
17
17
 
18
18
  interface TaskInfo {
19
- status: TaskStatus;
20
- agentName: string;
21
- prompt: string;
22
- options?: BackgroundTaskOptions;
23
- result?: unknown;
24
- error?: unknown;
25
- callbacks: Set<CompletionCallback>;
19
+ status: TaskStatus
20
+ agentName: string
21
+ prompt: string
22
+ options?: BackgroundTaskOptions
23
+ result?: unknown
24
+ error?: unknown
25
+ callbacks: Set<CompletionCallback>
26
26
  }
27
27
 
28
- type Dispatcher = (
28
+ export type Dispatcher = (
29
29
  agentName: string,
30
30
  prompt: string,
31
31
  options?: BackgroundTaskOptions,
32
- ) => Promise<string>;
32
+ ) => Promise<string>
33
33
 
34
34
  export function createBackgroundManager(
35
- dispatcher: Dispatcher,
35
+ initialDispatcher: Dispatcher,
36
+ options?: { maxConcurrent?: number },
36
37
  ): BackgroundManagerWithTaskCallbacks {
37
- const logger = createLogger();
38
- const tasks = new Map<string, TaskInfo>();
39
- const queue: string[] = [];
40
- const globalCallbacks = new Set<CompletionCallback>();
41
-
42
- let runningCount = 0;
43
- let maxConcurrent = 3;
44
- let taskCount = 0;
38
+ const logger = createLogger()
39
+ const tasks = new Map<string, TaskInfo>()
40
+ const queue: string[] = []
41
+ const globalCallbacks = new Set<CompletionCallback>()
42
+
43
+ const dispatcher = initialDispatcher
44
+ let runningCount = 0
45
+ const maxConcurrent = options?.maxConcurrent ?? 3
46
+ let taskCount = 0
47
+
48
+ function safeInvokeCallback(callback: CompletionCallback, taskId: string, result: unknown): void {
49
+ try {
50
+ callback(taskId, result)
51
+ } catch (error: unknown) {
52
+ logger.error(`Background callback failed: ${taskId}`, error)
53
+ }
54
+ }
45
55
 
46
56
  function invokeCallbacks(taskId: string, result: unknown): void {
47
- const task = tasks.get(taskId);
57
+ const task = tasks.get(taskId)
48
58
  if (!task) {
49
- return;
59
+ return
50
60
  }
51
61
 
52
62
  for (const callback of globalCallbacks) {
53
- callback(taskId, result);
63
+ safeInvokeCallback(callback, taskId, result)
54
64
  }
55
65
 
56
66
  for (const callback of task.callbacks) {
57
- callback(taskId, result);
67
+ safeInvokeCallback(callback, taskId, result)
58
68
  }
59
69
 
60
- task.callbacks.clear();
70
+ task.callbacks.clear()
61
71
  }
62
72
 
63
73
  function processQueue(): void {
64
74
  while (runningCount < maxConcurrent && queue.length > 0) {
65
- const nextTaskId = queue.shift();
75
+ const nextTaskId = queue.shift()
66
76
 
67
77
  if (!nextTaskId) {
68
- return;
78
+ return
69
79
  }
70
80
 
71
- const task = tasks.get(nextTaskId);
81
+ const task = tasks.get(nextTaskId)
72
82
  if (!task || task.status === "cancelled") {
73
- continue;
83
+ continue
74
84
  }
75
85
 
76
- task.status = "running";
77
- runningCount += 1;
86
+ task.status = "running"
87
+ runningCount += 1
78
88
 
79
89
  dispatcher(task.agentName, task.prompt, task.options)
80
90
  .then((result) => {
81
- const currentTask = tasks.get(nextTaskId);
91
+ const currentTask = tasks.get(nextTaskId)
82
92
 
83
93
  if (!currentTask || currentTask.status === "cancelled") {
84
- return;
94
+ return
85
95
  }
86
96
 
87
- currentTask.status = "completed";
88
- currentTask.result = result;
89
- invokeCallbacks(nextTaskId, result);
97
+ currentTask.status = "completed"
98
+ currentTask.result = result
99
+ invokeCallbacks(nextTaskId, result)
90
100
  })
91
101
  .catch((error: unknown) => {
92
- const currentTask = tasks.get(nextTaskId);
102
+ const currentTask = tasks.get(nextTaskId)
93
103
 
94
104
  if (!currentTask || currentTask.status === "cancelled") {
95
- return;
105
+ return
96
106
  }
97
107
 
98
- currentTask.status = "failed";
99
- currentTask.error = error;
100
- logger.error(`Background task failed: ${nextTaskId}`, error);
108
+ currentTask.status = "failed"
109
+ currentTask.error = error
110
+ logger.error(`Background task failed: ${nextTaskId}`, error)
111
+ invokeCallbacks(nextTaskId, error)
101
112
  })
102
113
  .finally(() => {
103
- runningCount = Math.max(0, runningCount - 1);
104
- processQueue();
105
- });
114
+ runningCount = Math.max(0, runningCount - 1)
115
+ processQueue()
116
+ })
106
117
  }
107
118
  }
108
119
 
109
- function dispatch(
110
- agentName: string,
111
- prompt: string,
112
- options?: BackgroundTaskOptions,
113
- ): string {
114
- if (typeof options?.max_concurrent === "number" && options.max_concurrent > 0) {
115
- maxConcurrent = options.max_concurrent;
116
- }
117
-
118
- taskCount += 1;
119
- const taskId = `task-${taskCount}`;
120
+ function dispatch(agentName: string, prompt: string, options?: BackgroundTaskOptions): string {
121
+ taskCount += 1
122
+ const taskId = `task-${taskCount}`
120
123
 
121
124
  tasks.set(taskId, {
122
125
  status: "queued",
@@ -124,70 +127,73 @@ export function createBackgroundManager(
124
127
  prompt,
125
128
  options,
126
129
  callbacks: new Set<CompletionCallback>(),
127
- });
130
+ })
128
131
 
129
- queue.push(taskId);
130
- processQueue();
132
+ queue.push(taskId)
133
+ processQueue()
131
134
 
132
- return taskId;
135
+ return taskId
133
136
  }
134
137
 
135
138
  function cancel(taskId: string): void {
136
- const task = tasks.get(taskId);
139
+ const task = tasks.get(taskId)
137
140
  if (!task) {
138
- return;
141
+ return
139
142
  }
140
143
 
141
- task.status = "cancelled";
144
+ task.status = "cancelled"
142
145
 
143
- const queuedTaskIndex = queue.indexOf(taskId);
146
+ const queuedTaskIndex = queue.indexOf(taskId)
144
147
  if (queuedTaskIndex >= 0) {
145
- queue.splice(queuedTaskIndex, 1);
148
+ queue.splice(queuedTaskIndex, 1)
146
149
  }
147
150
  }
148
151
 
149
152
  function getResult(taskId: string): Promise<unknown> {
150
- const task = tasks.get(taskId);
153
+ const task = tasks.get(taskId)
151
154
  if (!task || task.status !== "completed") {
152
- return Promise.resolve(undefined);
155
+ return Promise.resolve(undefined)
153
156
  }
154
157
 
155
- return Promise.resolve(task.result);
158
+ return Promise.resolve(task.result)
156
159
  }
157
160
 
158
- function onComplete(taskIdOrCallback: string | CompletionCallback, callback?: CompletionCallback): void {
161
+ function onComplete(
162
+ taskIdOrCallback: string | CompletionCallback,
163
+ callback?: CompletionCallback,
164
+ ): void {
159
165
  if (typeof taskIdOrCallback === "function") {
160
- globalCallbacks.add(taskIdOrCallback);
161
- return;
166
+ globalCallbacks.add(taskIdOrCallback)
167
+ return
162
168
  }
163
169
 
164
170
  if (!callback) {
165
- return;
171
+ return
166
172
  }
167
173
 
168
- const task = tasks.get(taskIdOrCallback);
174
+ const task = tasks.get(taskIdOrCallback)
169
175
  if (!task) {
170
- return;
176
+ return
171
177
  }
172
178
 
173
179
  if (task.status === "completed") {
174
- callback(taskIdOrCallback, task.result);
175
- return;
180
+ safeInvokeCallback(callback, taskIdOrCallback, task.result)
181
+ return
176
182
  }
177
183
 
178
- task.callbacks.add(callback);
184
+ task.callbacks.add(callback)
179
185
  }
180
186
 
181
187
  function getActiveCount(): number {
182
- let activeCount = 0;
188
+ let activeCount = 0
183
189
 
184
190
  for (const task of tasks.values()) {
185
191
  if (task.status === "queued" || task.status === "running") {
186
- activeCount += 1;
192
+ activeCount += 1
187
193
  }
188
194
  }
189
195
 
190
- return activeCount;
196
+ return activeCount
191
197
  }
192
198
 
193
199
  return {
@@ -196,5 +202,5 @@ export function createBackgroundManager(
196
202
  getResult,
197
203
  onComplete,
198
204
  getActiveCount,
199
- };
205
+ }
200
206
  }
@@ -1 +1 @@
1
- export * from "./background-manager";
1
+ export * from "./background-manager"
@@ -1,8 +1,8 @@
1
- import type { AuditState } from "../../state/types"
2
1
  import { createLogger } from "../../shared/logger"
2
+ import type { AuditState } from "../../state/types"
3
3
 
4
4
  const DEFAULT_MAX_TOKENS = 200_000
5
- const REMINDER_THRESHOLD = 0.70
5
+ const REMINDER_THRESHOLD = 0.7
6
6
  const COMPACTION_THRESHOLD = 0.85
7
7
 
8
8
  export interface ContextMonitorConfig {
@@ -19,7 +19,7 @@ export function createContextMonitor(config: ContextMonitorConfig = {}) {
19
19
 
20
20
  function getContextStatus(
21
21
  systemText: string,
22
- auditState: AuditState | null,
22
+ _auditState: AuditState | null,
23
23
  ): { usage: number; reminder: string | null; shouldCompact: boolean } {
24
24
  const tokens = estimateTokens(systemText)
25
25
  const usage = tokens / maxTokens
@@ -1,4 +1,4 @@
1
- export { createContextMonitor } from "./context-monitor"
2
1
  export type { ContextMonitorConfig } from "./context-monitor"
3
- export { createToolOutputTruncator } from "./tool-output-truncator"
2
+ export { createContextMonitor } from "./context-monitor"
4
3
  export type { TruncatorConfig } from "./tool-output-truncator"
4
+ export { createToolOutputTruncator } from "./tool-output-truncator"
@@ -1,10 +1,8 @@
1
- import type { AuditState } from "../../state/types"
2
1
  import type { AuditStateManager } from "../../managers/types"
3
2
  import { createLogger } from "../../shared/logger"
3
+ import type { AuditState } from "../../state/types"
4
4
 
5
- export function createSessionRecoveryHandler(
6
- auditStateManager: AuditStateManager,
7
- ) {
5
+ export function createSessionRecoveryHandler(auditStateManager: AuditStateManager) {
8
6
  const logger = createLogger()
9
7
 
10
8
  return async (event: {
@@ -10,17 +10,17 @@ const TOOL_FALLBACKS: Record<string, ToolFallbackEntry> = {
10
10
  slither: {
11
11
  install: "pip install slither-analyzer",
12
12
  fallback:
13
- "Slither is unavailable. PROCEED with the audit using `argus_analyze_contract` for structural profiling and `argus_check_patterns` for vulnerability scanning. Note in the final report: \"Automated static analysis (Slither) was unavailable; manual review intensity increased.\"",
13
+ 'Slither is unavailable. PROCEED with the audit using `argus_analyze_contract` for structural profiling and `argus_check_patterns` for vulnerability scanning. Note in the final report: "Automated static analysis (Slither) was unavailable; manual review intensity increased."',
14
14
  },
15
15
  forge: {
16
16
  install: "curl -L https://foundry.paradigm.xyz | bash && foundryup",
17
17
  fallback:
18
- "Foundry/Forge is unavailable. SKIP automated testing and fuzzing. Verify findings through manual code tracing and static analysis. Note in the final report: \"Dynamic testing (Forge) was unavailable; findings verified via manual analysis.\"",
18
+ 'Foundry/Forge is unavailable. SKIP automated testing and fuzzing. Verify findings through manual code tracing and static analysis. Note in the final report: "Dynamic testing (Forge) was unavailable; findings verified via manual analysis."',
19
19
  },
20
20
  solodit: {
21
21
  install: "",
22
22
  fallback:
23
- "Solodit API is unreachable. PROCEED using `argus_check_patterns` with local vulnerability rules. Note in the final report: \"External vulnerability databases were inaccessible; research limited to local patterns.\"",
23
+ 'Solodit API is unreachable. PROCEED using `argus_check_patterns` with local vulnerability rules. Note in the final report: "External vulnerability databases were inaccessible; research limited to local patterns."',
24
24
  },
25
25
  scvd: {
26
26
  install: "",
@@ -54,6 +54,7 @@ function resolveToolBase(tool: string): string {
54
54
 
55
55
  export function createToolErrorRecoveryHandler(
56
56
  getAuditState?: () => AuditState | null,
57
+ updateAuditState?: (patch: Partial<AuditState>) => Promise<void>,
57
58
  ) {
58
59
  const logger = createLogger()
59
60
 
@@ -80,12 +81,16 @@ export function createToolErrorRecoveryHandler(
80
81
 
81
82
  const unavailable = isToolUnavailable(lowerResult)
82
83
 
83
- if (unavailable && getAuditState) {
84
+ if (unavailable && getAuditState && updateAuditState) {
84
85
  const state = getAuditState()
85
86
  if (state) {
86
- state.unavailableTools ??= []
87
- if (!state.unavailableTools.includes(toolBase)) {
88
- state.unavailableTools.push(toolBase)
87
+ const existing = state.unavailableTools ?? []
88
+ if (!existing.includes(toolBase)) {
89
+ void updateAuditState({
90
+ unavailableTools: [...existing, toolBase],
91
+ }).catch((error: unknown) => {
92
+ logger.warn(`Failed to persist unavailable tool state for ${toolBase}`, error)
93
+ })
89
94
  logger.info(`Recorded ${toolBase} as unavailable — fallback activated`)
90
95
  }
91
96
  }
@@ -1,5 +1,5 @@
1
- export * from './background-agent'
2
- export * from './persistent-state'
3
- export * from './context-monitor'
4
- export * from './audit-enforcer'
5
- export * from './error-recovery'
1
+ export * from "./audit-enforcer"
2
+ export * from "./background-agent"
3
+ export * from "./context-monitor"
4
+ export * from "./error-recovery"
5
+ export * from "./persistent-state"