solidity-argus 0.1.8 → 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.
- package/AGENTS.md +3 -3
- package/README.md +229 -13
- package/package.json +37 -8
- package/skills/INVENTORY.md +88 -57
- package/skills/README.md +72 -6
- package/skills/case-studies/beanstalk-governance/SKILL.md +52 -0
- package/skills/case-studies/bzx-flash-loan/SKILL.md +53 -0
- package/skills/case-studies/cream-finance/SKILL.md +52 -0
- package/skills/case-studies/curve-reentrancy/SKILL.md +52 -0
- package/skills/case-studies/dao-hack/SKILL.md +51 -0
- package/skills/case-studies/euler-finance/SKILL.md +52 -0
- package/skills/case-studies/harvest-finance/SKILL.md +52 -0
- package/skills/case-studies/level-finance/SKILL.md +51 -0
- package/skills/case-studies/mango-markets/SKILL.md +53 -0
- package/skills/case-studies/nomad-bridge/SKILL.md +51 -0
- package/skills/case-studies/parity-multisig/SKILL.md +55 -0
- package/skills/case-studies/poly-network/SKILL.md +51 -0
- package/skills/case-studies/rari-fuse/SKILL.md +51 -0
- package/skills/case-studies/ronin-bridge/SKILL.md +52 -0
- package/skills/case-studies/wormhole-bridge/SKILL.md +51 -0
- package/skills/checklists/cyfrin-defi-core/SKILL.md +3 -0
- package/skills/manifests/cyfrin.json +16 -0
- package/skills/manifests/defifofum.json +25 -0
- package/skills/manifests/kadenzipfel.json +48 -0
- package/skills/manifests/scvd.json +9 -0
- package/skills/manifests/smartbugs.json +9 -0
- package/skills/manifests/solodit.json +9 -0
- package/skills/manifests/sunweb3sec.json +9 -0
- package/skills/manifests/trailofbits.json +9 -0
- package/skills/methodology/audit-workflow/SKILL.md +3 -0
- package/skills/protocol-patterns/amm-dex/SKILL.md +3 -0
- package/skills/references/exploit-reference/SKILL.md +3 -0
- package/skills/vulnerability-patterns/access-control/SKILL.md +27 -0
- package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +13 -1
- package/skills/vulnerability-patterns/assert-violation/SKILL.md +8 -1
- package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +12 -1
- package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +8 -1
- package/skills/vulnerability-patterns/cross-chain-bridge-vulnerabilities/SKILL.md +217 -0
- package/skills/vulnerability-patterns/default-visibility/SKILL.md +13 -1
- package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +8 -1
- package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +8 -1
- package/skills/vulnerability-patterns/dos-revert/SKILL.md +14 -1
- package/skills/vulnerability-patterns/erc4626-exchange-rate-manipulation/SKILL.md +64 -0
- package/skills/vulnerability-patterns/fee-on-transfer-tokens/SKILL.md +93 -0
- package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +13 -0
- package/skills/vulnerability-patterns/floating-pragma/SKILL.md +8 -1
- package/skills/vulnerability-patterns/front-running-attacks/SKILL.md +209 -0
- package/skills/vulnerability-patterns/gas-optimization-patterns/SKILL.md +203 -0
- package/skills/vulnerability-patterns/governance-attacks/SKILL.md +208 -0
- package/skills/vulnerability-patterns/hash-collision/SKILL.md +8 -1
- package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +12 -1
- package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +8 -1
- package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +8 -1
- package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +12 -1
- package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +7 -1
- package/skills/vulnerability-patterns/logic-errors/SKILL.md +10 -0
- package/skills/vulnerability-patterns/missing-parameter-bounds/SKILL.md +44 -0
- package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +17 -1
- package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +12 -1
- package/skills/vulnerability-patterns/off-by-one/SKILL.md +7 -1
- package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +22 -0
- package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +8 -1
- package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +11 -1
- package/skills/vulnerability-patterns/proxy-vulnerabilities/SKILL.md +209 -0
- package/skills/vulnerability-patterns/reentrancy/SKILL.md +22 -0
- package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +8 -1
- package/skills/vulnerability-patterns/share-accounting-desynchronization/SKILL.md +44 -0
- package/skills/vulnerability-patterns/signature-malleability/SKILL.md +11 -1
- package/skills/vulnerability-patterns/stateful-parameter-update-drift/SKILL.md +44 -0
- package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +13 -1
- package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +8 -1
- package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +8 -1
- package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +8 -1
- package/skills/vulnerability-patterns/unsafe-erc20-transfers/SKILL.md +132 -0
- package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +11 -1
- package/skills/vulnerability-patterns/unused-variables/SKILL.md +8 -1
- package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +8 -1
- package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +8 -1
- package/skills/vulnerability-patterns/weird-tokens/SKILL.md +10 -0
- package/skills/vulnerability-patterns/zero-address-misconfiguration/SKILL.md +48 -0
- package/src/agents/argus-prompt.ts +27 -10
- package/src/agents/pythia-prompt.ts +7 -8
- package/src/agents/scribe-prompt.ts +10 -5
- package/src/agents/sentinel-prompt.ts +36 -7
- package/src/cli/cli-output.ts +16 -0
- package/src/cli/cli-program.ts +29 -22
- package/src/cli/commands/check-skills.ts +135 -0
- package/src/cli/commands/doctor.ts +303 -23
- package/src/cli/commands/init.ts +8 -6
- package/src/cli/commands/install.ts +10 -8
- package/src/cli/commands/lint-skills.ts +118 -0
- package/src/cli/index.ts +5 -5
- package/src/cli/tui-prompts.ts +4 -2
- package/src/cli/types.ts +3 -3
- package/src/config/index.ts +1 -1
- package/src/config/loader.ts +4 -6
- package/src/config/schema.ts +6 -5
- package/src/config/types.ts +2 -2
- package/src/constants/defaults.ts +2 -0
- package/src/create-hooks.ts +225 -29
- package/src/create-managers.ts +10 -8
- package/src/create-tools.ts +14 -8
- package/src/features/background-agent/background-manager.ts +93 -87
- package/src/features/background-agent/index.ts +1 -1
- package/src/features/context-monitor/context-monitor.ts +3 -3
- package/src/features/context-monitor/index.ts +2 -2
- package/src/features/error-recovery/session-recovery.ts +2 -4
- package/src/features/error-recovery/tool-error-recovery.ts +79 -19
- package/src/features/index.ts +5 -5
- package/src/features/persistent-state/audit-state-manager.ts +158 -52
- package/src/features/persistent-state/global-run-index.ts +38 -0
- package/src/features/persistent-state/index.ts +1 -1
- package/src/features/persistent-state/run-journal.ts +86 -0
- package/src/hooks/agent-tracker.ts +53 -0
- package/src/hooks/compaction-hook.ts +46 -37
- package/src/hooks/config-handler.ts +31 -11
- package/src/hooks/context-budget.ts +42 -0
- package/src/hooks/event-hook.ts +48 -23
- package/src/hooks/hook-system.ts +4 -4
- package/src/hooks/index.ts +5 -5
- package/src/hooks/knowledge-sync-hook.ts +19 -21
- package/src/hooks/recon-context-builder.ts +66 -0
- package/src/hooks/safe-create-hook.ts +9 -11
- package/src/hooks/system-prompt-hook.ts +128 -0
- package/src/hooks/tool-tracking-hook.ts +162 -29
- package/src/hooks/types.ts +2 -1
- package/src/index.ts +23 -13
- package/src/knowledge/retry.ts +53 -0
- package/src/knowledge/scvd-client.ts +103 -83
- package/src/knowledge/scvd-errors.ts +89 -0
- package/src/knowledge/scvd-index.ts +110 -62
- package/src/knowledge/scvd-sync.ts +223 -47
- package/src/knowledge/source-manifest.ts +102 -0
- package/src/managers/index.ts +1 -1
- package/src/managers/types.ts +19 -14
- package/src/plugin-interface.ts +19 -8
- package/src/shared/binary-utils.ts +44 -34
- package/src/shared/deep-merge.ts +55 -36
- package/src/shared/file-utils.ts +21 -19
- package/src/shared/index.ts +11 -5
- package/src/shared/jsonc-parser.ts +123 -28
- package/src/shared/logger.ts +91 -17
- package/src/shared/project-utils.ts +30 -0
- package/src/skills/analysis/cluster.ts +414 -0
- package/src/skills/analysis/gates.ts +227 -0
- package/src/skills/analysis/index.ts +33 -0
- package/src/skills/analysis/normalize.ts +217 -0
- package/src/skills/analysis/similarity.ts +224 -0
- package/src/skills/argus-skill-resolver.ts +237 -0
- package/src/skills/skill-schema.ts +99 -0
- package/src/solodit-lifecycle.ts +202 -0
- package/src/state/audit-state.ts +10 -8
- package/src/state/finding-store.ts +68 -55
- package/src/state/types.ts +96 -44
- package/src/tools/argus-skill-load-tool.ts +78 -0
- package/src/tools/contract-analyzer-tool.ts +60 -77
- package/src/tools/forge-coverage-tool.ts +226 -0
- package/src/tools/forge-fuzz-tool.ts +127 -127
- package/src/tools/forge-test-tool.ts +153 -157
- package/src/tools/gas-analysis-tool.ts +264 -0
- package/src/tools/pattern-checker-tool.ts +206 -167
- package/src/tools/pattern-loader.ts +77 -0
- package/src/tools/pattern-schema.ts +51 -0
- package/src/tools/proxy-detection-tool.ts +224 -0
- package/src/tools/report-generator-tool.ts +333 -142
- package/src/tools/slither-tool.ts +300 -210
- package/src/tools/solodit-search-tool.ts +255 -80
- package/src/tools/sync-knowledge-tool.ts +7 -11
- package/src/utils/audit-artifact-detector.ts +118 -0
- package/src/utils/dependency-scanner.ts +93 -0
- package/src/utils/project-detector.ts +175 -86
- package/src/utils/solidity-parser.ts +112 -67
- package/src/utils/solodit-health.ts +29 -0
- package/src/hooks/event-hook-v2.ts +0 -99
- package/src/state/plugin-state.ts +0 -14
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context budget allocation for Argus agents.
|
|
3
|
+
*
|
|
4
|
+
* Provides per-agent token budgets for system-prompt injection sizing.
|
|
5
|
+
* Under context pressure (>70%), budgets are reduced by 50% to prevent
|
|
6
|
+
* context window overflow during long audits.
|
|
7
|
+
*
|
|
8
|
+
* Decoupled from system-prompt-hook.ts — consumed by the hook when available.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const ARGUS_BUDGET = 2000
|
|
12
|
+
const SUBAGENT_BUDGET = 1000
|
|
13
|
+
const PRESSURE_THRESHOLD = 0.7
|
|
14
|
+
const PRESSURE_REDUCTION = 0.5
|
|
15
|
+
|
|
16
|
+
const ARGUS_AGENTS = new Set(["argus"])
|
|
17
|
+
const SUBAGENTS = new Set(["sentinel", "pythia", "scribe"])
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the token budget for a given agent, adjusted for context pressure.
|
|
21
|
+
*
|
|
22
|
+
* @param agent - Agent name (e.g. "argus", "sentinel", "pythia", "scribe")
|
|
23
|
+
* @param contextPressure - Current context usage ratio (0.0–1.0), from ContextMonitor
|
|
24
|
+
* @returns Token budget in tokens. 0 for non-Argus agents.
|
|
25
|
+
*/
|
|
26
|
+
export function getTokenBudgetForAgent(agent: string, contextPressure: number = 0): number {
|
|
27
|
+
let budget: number
|
|
28
|
+
|
|
29
|
+
if (ARGUS_AGENTS.has(agent)) {
|
|
30
|
+
budget = ARGUS_BUDGET
|
|
31
|
+
} else if (SUBAGENTS.has(agent)) {
|
|
32
|
+
budget = SUBAGENT_BUDGET
|
|
33
|
+
} else {
|
|
34
|
+
return 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (contextPressure > PRESSURE_THRESHOLD) {
|
|
38
|
+
budget = Math.floor(budget * PRESSURE_REDUCTION)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return budget
|
|
42
|
+
}
|
package/src/hooks/event-hook.ts
CHANGED
|
@@ -1,32 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createLogger } from "../shared/logger"
|
|
2
2
|
import { createAuditState } from "../state/audit-state"
|
|
3
|
+
import type { AuditState } from "../state/types"
|
|
4
|
+
|
|
5
|
+
export type AuditEventType =
|
|
6
|
+
| "session.created"
|
|
7
|
+
| "session.idle"
|
|
8
|
+
| "session.error"
|
|
9
|
+
| "session.deleted"
|
|
10
|
+
| "audit.phase-changed"
|
|
11
|
+
| "audit.finding-added"
|
|
12
|
+
| "audit.complete"
|
|
3
13
|
|
|
4
14
|
export type EventHookFn = (input: {
|
|
5
|
-
event: { type: string; sessionId?: string }
|
|
15
|
+
event: { type: string; sessionId?: string; properties?: Record<string, unknown> }
|
|
16
|
+
}) => Promise<void>
|
|
17
|
+
|
|
18
|
+
export type EventSubHandler = (event: {
|
|
19
|
+
type: string
|
|
20
|
+
sessionId?: string
|
|
21
|
+
auditState: AuditState | null
|
|
22
|
+
setAuditState: (state: AuditState | null) => void
|
|
6
23
|
}) => Promise<void>
|
|
7
24
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* closure-held audit state. Other hooks (compaction, tool tracking,
|
|
13
|
-
* system prompt) share the same state instance via these accessors.
|
|
14
|
-
*/
|
|
15
|
-
export function createEventHook(projectDir?: string): {
|
|
25
|
+
export function createEventHook(
|
|
26
|
+
projectDir?: string,
|
|
27
|
+
subHandlers: EventSubHandler[] = [],
|
|
28
|
+
): {
|
|
16
29
|
hook: EventHookFn
|
|
17
30
|
getAuditState: () => AuditState | null
|
|
18
31
|
setAuditState: (state: AuditState | null) => void
|
|
19
32
|
} {
|
|
33
|
+
const logger = createLogger()
|
|
20
34
|
let currentAuditState: AuditState | null = null
|
|
21
35
|
|
|
22
36
|
const getAuditState = (): AuditState | null => currentAuditState
|
|
23
|
-
|
|
24
37
|
const setAuditState = (state: AuditState | null): void => {
|
|
25
38
|
currentAuditState = state
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
const hook: EventHookFn = async (input): Promise<void> => {
|
|
29
|
-
const { type } = input.event
|
|
42
|
+
const { type, sessionId } = input.event
|
|
30
43
|
|
|
31
44
|
switch (type) {
|
|
32
45
|
case "session.created": {
|
|
@@ -37,23 +50,23 @@ export function createEventHook(projectDir?: string): {
|
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
case "session.idle": {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
if (currentAuditState) {
|
|
54
|
+
logger.debug(
|
|
55
|
+
`Session idle — phase: ${currentAuditState.currentPhase}, findings: ${currentAuditState.findings.length}`,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
break
|
|
59
|
+
}
|
|
47
60
|
|
|
48
61
|
case "session.error": {
|
|
49
62
|
if (currentAuditState) {
|
|
50
|
-
|
|
51
|
-
`
|
|
63
|
+
logger.error(
|
|
64
|
+
`Session error — state snapshot: ${JSON.stringify({
|
|
52
65
|
sessionId: currentAuditState.sessionId,
|
|
53
66
|
phase: currentAuditState.currentPhase,
|
|
54
67
|
findingsCount: currentAuditState.findings.length,
|
|
55
68
|
contractsReviewed: currentAuditState.contractsReviewed,
|
|
56
|
-
})}
|
|
69
|
+
})}`,
|
|
57
70
|
)
|
|
58
71
|
}
|
|
59
72
|
break
|
|
@@ -64,10 +77,22 @@ export function createEventHook(projectDir?: string): {
|
|
|
64
77
|
break
|
|
65
78
|
}
|
|
66
79
|
|
|
67
|
-
// Unknown events: no-op — never throw
|
|
68
80
|
default:
|
|
69
81
|
break
|
|
70
82
|
}
|
|
83
|
+
|
|
84
|
+
for (const handler of subHandlers) {
|
|
85
|
+
try {
|
|
86
|
+
await handler({
|
|
87
|
+
type,
|
|
88
|
+
sessionId,
|
|
89
|
+
auditState: currentAuditState,
|
|
90
|
+
setAuditState,
|
|
91
|
+
})
|
|
92
|
+
} catch (error) {
|
|
93
|
+
logger.error(`Sub-handler failed for event ${type}:`, error)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
71
96
|
}
|
|
72
97
|
|
|
73
98
|
return { hook, getAuditState, setAuditState }
|
package/src/hooks/hook-system.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { HookName } from "./types"
|
|
1
|
+
import type { HookName } from "./types"
|
|
2
2
|
|
|
3
3
|
export function createHookGuard(disabledHooks: string[]) {
|
|
4
|
-
const disabledSet = new Set(disabledHooks)
|
|
4
|
+
const disabledSet = new Set(disabledHooks)
|
|
5
5
|
|
|
6
6
|
return function isHookEnabled(name: HookName): boolean {
|
|
7
|
-
return !disabledSet.has(name)
|
|
8
|
-
}
|
|
7
|
+
return !disabledSet.has(name)
|
|
8
|
+
}
|
|
9
9
|
}
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export
|
|
5
|
-
export type {
|
|
1
|
+
export type { AuditEventType, EventHookFn, EventSubHandler } from "./event-hook"
|
|
2
|
+
export { createEventHook } from "./event-hook"
|
|
3
|
+
export { createHookGuard } from "./hook-system"
|
|
4
|
+
export { safeCreateHook } from "./safe-create-hook"
|
|
5
|
+
export type { HookName } from "./types"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import os from "node:os"
|
|
2
2
|
import path from "node:path"
|
|
3
|
-
import { ScvdClient } from "../knowledge/scvd-client"
|
|
4
|
-
import { syncIncremental, type SyncResult } from "../knowledge/scvd-sync"
|
|
5
3
|
import type { ArgusConfig } from "../config/types"
|
|
4
|
+
import { ScvdClient } from "../knowledge/scvd-client"
|
|
5
|
+
import { type SyncResult, syncIncremental } from "../knowledge/scvd-sync"
|
|
6
|
+
import { createLogger } from "../shared/logger"
|
|
6
7
|
|
|
7
8
|
export type KnowledgeSyncDependencies = {
|
|
8
9
|
createClient?: (apiUrl: string) => unknown
|
|
@@ -18,14 +19,14 @@ function defaultDependencies(): Required<KnowledgeSyncDependencies> {
|
|
|
18
19
|
syncIncrementalFn: async (client: unknown, indexPath: string) =>
|
|
19
20
|
syncIncremental(client as ScvdClient, indexPath),
|
|
20
21
|
log: (message: string) => {
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
createLogger().info(message)
|
|
23
|
+
},
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export function createKnowledgeSyncHook(
|
|
27
28
|
argusConfig: ArgusConfig,
|
|
28
|
-
deps: KnowledgeSyncDependencies = {}
|
|
29
|
+
deps: KnowledgeSyncDependencies = {},
|
|
29
30
|
): () => void {
|
|
30
31
|
const dependencies = { ...defaultDependencies(), ...deps }
|
|
31
32
|
|
|
@@ -35,23 +36,20 @@ export function createKnowledgeSyncHook(
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const apiUrl = argusConfig.knowledge?.scvd?.apiUrl ?? DEFAULT_SCVD_API_URL
|
|
38
|
-
const indexPath = path.join(
|
|
39
|
-
os.homedir(),
|
|
40
|
-
".cache",
|
|
41
|
-
"solidity-argus",
|
|
42
|
-
"scvd-index.json"
|
|
43
|
-
)
|
|
39
|
+
const indexPath = path.join(os.homedir(), ".cache", "solidity-argus", "scvd-index.json")
|
|
44
40
|
|
|
45
41
|
Promise.resolve().then(async () => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
try {
|
|
43
|
+
const client = dependencies.createClient(apiUrl)
|
|
44
|
+
const result = await dependencies.syncIncrementalFn(client, indexPath)
|
|
45
|
+
if (result.newFindings > 0) {
|
|
46
|
+
dependencies.log(
|
|
47
|
+
`[argus] SCVD index updated: ${result.newFindings} new findings (total: ${result.totalIndexed})`,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
} catch (_e) {
|
|
51
|
+
createLogger().debug("Knowledge sync failed during auto-sync")
|
|
52
|
+
}
|
|
53
|
+
})
|
|
56
54
|
}
|
|
57
55
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { AuditArtifact } from "../utils/audit-artifact-detector"
|
|
2
|
+
import type { DependencyRisk } from "../utils/dependency-scanner"
|
|
3
|
+
import type { ProjectConfig } from "../utils/project-detector"
|
|
4
|
+
|
|
5
|
+
export interface ReconContext {
|
|
6
|
+
projectConfig: ProjectConfig | null
|
|
7
|
+
dependencyRisks: DependencyRisk[]
|
|
8
|
+
auditArtifacts: AuditArtifact[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Builds an XML-like reconnaissance context block from project data.
|
|
13
|
+
* Returns null if no data is available (all fields empty/null).
|
|
14
|
+
*
|
|
15
|
+
* The block is injected into compaction output so Argus agents retain
|
|
16
|
+
* project intelligence across context window compressions.
|
|
17
|
+
*/
|
|
18
|
+
export function buildReconContextBlock(recon: ReconContext): string | null {
|
|
19
|
+
if (
|
|
20
|
+
!recon.projectConfig &&
|
|
21
|
+
recon.dependencyRisks.length === 0 &&
|
|
22
|
+
recon.auditArtifacts.length === 0
|
|
23
|
+
) {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const lines: string[] = ["<argus-recon>"]
|
|
28
|
+
|
|
29
|
+
if (recon.projectConfig) {
|
|
30
|
+
const frameworks: string[] = []
|
|
31
|
+
if (recon.projectConfig.hasFoundry) frameworks.push("Foundry")
|
|
32
|
+
if (recon.projectConfig.hasHardhat) frameworks.push("Hardhat")
|
|
33
|
+
if (frameworks.length > 0) {
|
|
34
|
+
lines.push(`Framework: ${frameworks.join(", ")}`)
|
|
35
|
+
}
|
|
36
|
+
if (recon.projectConfig.optimizer) {
|
|
37
|
+
lines.push(`Optimizer: runs=${recon.projectConfig.optimizer.runs}`)
|
|
38
|
+
}
|
|
39
|
+
if (recon.projectConfig.evmVersion) {
|
|
40
|
+
lines.push(`EVM Version: ${recon.projectConfig.evmVersion}`)
|
|
41
|
+
}
|
|
42
|
+
if (recon.projectConfig.isUpgradeable) {
|
|
43
|
+
lines.push(`Upgradeable: yes`)
|
|
44
|
+
}
|
|
45
|
+
if (recon.projectConfig.profiles && recon.projectConfig.profiles.length > 0) {
|
|
46
|
+
lines.push(`Profiles: ${recon.projectConfig.profiles.join(", ")}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (recon.dependencyRisks.length > 0) {
|
|
51
|
+
lines.push("Dependency Risks:")
|
|
52
|
+
for (const risk of recon.dependencyRisks.slice(0, 5)) {
|
|
53
|
+
lines.push(` - ${risk.package}@${risk.version}: ${risk.risk}`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (recon.auditArtifacts.length > 0) {
|
|
58
|
+
lines.push("Existing Audit Artifacts:")
|
|
59
|
+
for (const artifact of recon.auditArtifacts.slice(0, 5)) {
|
|
60
|
+
lines.push(` - ${artifact.type}: ${artifact.path}`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
lines.push("</argus-recon>")
|
|
65
|
+
return lines.join("\n")
|
|
66
|
+
}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
): T | undefined {
|
|
1
|
+
import { createLogger } from "../shared/logger"
|
|
2
|
+
|
|
3
|
+
export function safeCreateHook<T>(factory: () => T, hookName: string): T | undefined {
|
|
5
4
|
try {
|
|
6
|
-
return factory()
|
|
5
|
+
return factory()
|
|
7
6
|
} catch (error) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return undefined;
|
|
7
|
+
const logger = createLogger()
|
|
8
|
+
logger.error(
|
|
9
|
+
`Failed to create hook "${hookName}": ${error instanceof Error ? error.message : String(error)}`,
|
|
10
|
+
)
|
|
11
|
+
return undefined
|
|
14
12
|
}
|
|
15
13
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { AuditState, FindingSeverity } from "../state/types"
|
|
2
|
+
|
|
3
|
+
const DEFAULT_TOKEN_BUDGET = 2000
|
|
4
|
+
const TOKENS_PER_CHAR = 4
|
|
5
|
+
|
|
6
|
+
export interface SystemPromptHookDeps {
|
|
7
|
+
getAuditState: () => AuditState | null
|
|
8
|
+
getAgentForSession: (sessionID: string) => string | undefined
|
|
9
|
+
isArgusAgent: (sessionID: string) => boolean
|
|
10
|
+
getContextPressure?: (systemText: string) => number
|
|
11
|
+
getTokenBudget?: (agent: string, contextPressure: number) => number
|
|
12
|
+
getEnforcerReminder?: (state: AuditState) => string | null
|
|
13
|
+
getReconBlock?: () => string | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const FALLBACK_DIRECTIVES: Record<string, string> = {
|
|
17
|
+
slither:
|
|
18
|
+
"DO NOT re-attempt argus_slither_analyze. Use `argus_analyze_contract` and `argus_check_patterns` instead. Note limitation in report.",
|
|
19
|
+
forge:
|
|
20
|
+
"DO NOT re-attempt argus_forge_test or argus_forge_fuzz. Verify findings via manual code tracing. Note limitation in report.",
|
|
21
|
+
solodit:
|
|
22
|
+
"DO NOT re-attempt argus_solodit_search. Use `argus_check_patterns` with local rules. Note limitation in report.",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function buildFallbackDirectives(unavailableTools: string[]): string[] {
|
|
26
|
+
const directives: string[] = []
|
|
27
|
+
for (const tool of unavailableTools) {
|
|
28
|
+
const directive = FALLBACK_DIRECTIVES[tool]
|
|
29
|
+
if (directive) directives.push(directive)
|
|
30
|
+
}
|
|
31
|
+
return directives
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function estimateTokens(text: string): number {
|
|
35
|
+
return Math.ceil(text.length / TOKENS_PER_CHAR)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function buildDynamicContext(
|
|
39
|
+
auditState: AuditState,
|
|
40
|
+
agent: string,
|
|
41
|
+
tokenBudget: number = DEFAULT_TOKEN_BUDGET,
|
|
42
|
+
): string {
|
|
43
|
+
const severityCounts: Record<FindingSeverity, number> = {
|
|
44
|
+
Critical: 0,
|
|
45
|
+
High: 0,
|
|
46
|
+
Medium: 0,
|
|
47
|
+
Low: 0,
|
|
48
|
+
Informational: 0,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (const finding of auditState.findings) {
|
|
52
|
+
severityCounts[finding.severity]++
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const tools = auditState.toolsExecuted.map((tool) => tool.tool).join(", ") || "none"
|
|
56
|
+
const unavailable = auditState.unavailableTools ?? []
|
|
57
|
+
const lines: string[] = [
|
|
58
|
+
`<argus-context agent="${agent}">`,
|
|
59
|
+
`Phase: ${auditState.currentPhase}`,
|
|
60
|
+
`Contracts: ${auditState.contractsReviewed.length} reviewed`,
|
|
61
|
+
`Findings: Critical=${severityCounts.Critical} High=${severityCounts.High} Medium=${severityCounts.Medium} Low=${severityCounts.Low} Info=${severityCounts.Informational}`,
|
|
62
|
+
`Tools: ${tools}`,
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
if (unavailable.length > 0) {
|
|
66
|
+
lines.push(`Unavailable: ${unavailable.join(", ")}`)
|
|
67
|
+
lines.push(...buildFallbackDirectives(unavailable))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
lines.push("</argus-context>")
|
|
71
|
+
|
|
72
|
+
let summary = lines.join("\n")
|
|
73
|
+
|
|
74
|
+
if (estimateTokens(summary) > tokenBudget) {
|
|
75
|
+
summary = [
|
|
76
|
+
`<argus-context agent="${agent}">`,
|
|
77
|
+
`Phase: ${auditState.currentPhase} | Findings: ${auditState.findings.length} | Contracts: ${auditState.contractsReviewed.length}`,
|
|
78
|
+
"</argus-context>",
|
|
79
|
+
].join("\n")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return summary
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function createSystemPromptHook(deps: SystemPromptHookDeps) {
|
|
86
|
+
return async (
|
|
87
|
+
input: { sessionID?: string; model: unknown },
|
|
88
|
+
output: { system: string[] },
|
|
89
|
+
): Promise<void> => {
|
|
90
|
+
if (!input.sessionID) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!deps.isArgusAgent(input.sessionID)) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const auditState = deps.getAuditState()
|
|
99
|
+
if (!auditState) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const agent = deps.getAgentForSession(input.sessionID)
|
|
104
|
+
if (!agent) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const currentSystem = output.system.join("\n")
|
|
109
|
+
const pressure = deps.getContextPressure?.(currentSystem) ?? 0
|
|
110
|
+
const budget = deps.getTokenBudget?.(agent, pressure) ?? DEFAULT_TOKEN_BUDGET
|
|
111
|
+
|
|
112
|
+
output.system.push(buildDynamicContext(auditState, agent, budget))
|
|
113
|
+
|
|
114
|
+
if (deps.getReconBlock) {
|
|
115
|
+
const reconBlock = deps.getReconBlock()
|
|
116
|
+
if (reconBlock && estimateTokens(reconBlock) <= budget) {
|
|
117
|
+
output.system.push(reconBlock)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (agent === "argus" && deps.getEnforcerReminder) {
|
|
122
|
+
const reminder = deps.getEnforcerReminder(auditState)
|
|
123
|
+
if (reminder) {
|
|
124
|
+
output.system.push(reminder)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|